「旧URLのリダイレクト表、Excelでいただけますか」。リニューアル案件の序盤で必ず出るやり取りです。ただ、表のとおりに設定して公開したら終わり、にはならない。公開後もURLは動きます。記事のスラッグを直す、カテゴリを整理する、古いページを1本消す——そのたびに旧URLは増えていく。

自分のサイト(tsudzuri.com)のCMSに溜まっていた404の記録を数えたら、存在しないURLへのアクセスは1,211種類・延べ5,100件。ただし手当てが要るものは、そのうち十数件でした。旧URLの受け皿をどこに置くか、CMSに置くと何が自動になるか、このログをどう読むか。EmDash(Astro向けのヘッドレスCMS)の実装を読みながら整理します。

リダイレクトの置き場所は3つある

301が何をするか——旧URLに溜まった評価を新URLへ引き継ぐ——はリニューアルとURL移行の記事に書きました。ここでは「どこに書くか」を分けます。

  • サーバー(.htaccess):Apacheなら定番。ただしJamstackやCloudflare Workersの構成では、置く場所がそもそも無い。
  • エッジ(Cloudflareのリダイレクトルールなど):全リクエストの手前で効く。反面、編集画面はインフラ側にあり、触れる人が限られる。
  • CMSの中:URLを変えた本人が、同じ管理画面で受け皿も作れる。
旧URLを受けられる3つの層(サーバー・エッジ・CMS)とどれにも当たらなかった場合の404ログ記録を示した図

3つ目を選ぶ理由は、速さではなく担当者の一致です。スラッグを変えるのは編集者。旧URLが死ぬ原因も編集者の操作。受け皿が同じ画面にあれば、作業がそこで完結します。

EmDashはこれをAstroのミドルウェアとして持っています。パスを有効なルールと突き合わせ、当たれば Location を返す。ルールの実体はデータベースの行なので、ビルドもデプロイも要りません。権限は一覧が redirects:read、作成・編集が redirects:manage に分かれていて、誰に渡すかは管理画面の権限設計と同じ考え方で決めます。

スラッグを変えた瞬間に301が入る

EmDashで記事のスラッグを変えると、旧URLから新URLへの301が自動で1本作られます。手では書きません。実装を追うと、こう動いていました。

  • コレクションのURLパターン(/blog/{slug} など)から旧URLと新URLを組み立てる
  • 旧URLと新URLが同じなら何もしない
  • すでに旧URLを指していたルールは、新URLへ付け替える(301の数珠つなぎを作らない)
  • 新URLを source にしていたルールは消す(改名を元に戻したときにループしないため)
  • できたルールには Auto: slug change というグループ名が付く
スラッグ変更時にCMSの中で起きること4ステップ(自動301の生成・チェーンの付け替え・公開時反映)を示した図

下書きの段階でスラッグを変えた場合は、公開したタイミングで入ります。公開までは旧URLがまだ生きている。これで辻褄が合う。

数珠つなぎを潰す挙動は地味ですが、効きます。A→B→Cと2段になったリダイレクトは、途中の1本を消した瞬間に切れる。手作業でリダイレクト表を育てていると、ここが必ず崩れます。

まとめて受ける、きっぱり消す

1本ずつでは追いつかない移行もあります。/news/2019/05/xxx が数百本、といったケース。EmDashのルールにはAstroのルート構文をそのまま書けます。

/old-blog/[...path]  →  /blog/[...path]

[param] が1セグメント、[...rest] が残り全部。制約もはっきりしています。

  • [...rest] は最後のセグメントにしか置けない
  • プレースホルダはセグメント丸ごと。/news-[id] のように文字と混ぜると弾かれる
  • 移動先で使えるのは、移動元で拾った名前だけ

行き先が無いページを、無理にトップへ301で飛ばす必要はありません。消したのなら410(Gone)でいい。410は「意図して消した、もう戻らない」の意思表示で、404より早く索引から外れるとされています。EmDashは410と451を「行き先なしで返すステータス」として扱うので、移動先を空のままルールを作れる。

ひとつ穴があります。このミドルウェアは、拡張子の付いたパスを素通しします。静的ファイルを巻き込まないための判定ですが、/company/about.html のような旧サイト由来のURLも一緒に対象外になる。.html時代のサイトから引っ越すなら、その分はエッジ側で受ける前提で見積もります(WordPressからの移行でも最初に確認するところです)。

404ログは件数順ではなく参照元で読む

どのルールにも当たらず404になったパスは、そのまま記録されます。パスごとに1行にまとめ、同じパスが叩かれればカウントが増える。参照元とUser-Agentも残ります。行数は1万件が上限で、超えると最後に見た時刻が古い行から捨てられるので、溜め込んで後でまとめて見る使い方は想定されていません。

このログを直すべきURLの一覧として上から読むと、まず溺れます。うちの実データはこうでした(2026年5月〜9月)。

  • 記録されたパス 1,211種類・延べ5,100リクエスト
  • 上位は /wp-json/wp/v2/users/(237件)、/.git/config(159件)、/.env.development、/.aws/credentials、/actuator/env
  • 参照元が入っている行は12件だけ

WordPressで作っていないサイトに /wp-json/ が来る。上位はほぼ全部、設定ファイルや管理APIを探しに来たボットで、リダイレクトを作る相手ではありません。人がリンクを踏んで404に着いたときは、参照元が残ります。だから見るべきは件数順ではなく、参照元が入っている行。それだけで対象は1,211件から十数件に減ります。

404ログを読むときの4つの目安(件数順で見ない・参照元で絞る・1万件で頭打ち・302で逃がさない)を示した図

その12件を眺めていて、自分の実装の粗も見つかりました。このサイトは存在しない記事URLを Astro.redirect("/404") で404ページへ飛ばしています。302が1回挟まるので、ログに残るのは /404 という行だけ。実際に叩かれたURLが消えてしまう。参照元付き12件のうち最多だったのが、この /404 でした。存在しないURLは飛ばさず、その場で404を返す。ここは直します。

リダイレクトは、公開前に一度作って終わる作業ではありません。URLを変える人と、受け皿を作る人と、結果を見る場所が離れているほど漏れます。

移行やリニューアルで「旧URLの扱いをどう設計するか」から相談したい方は、お問い合わせからどうぞ。いまのサイトに404がどう溜まっているかを見るところからお手伝いします。

本記事は2026年9月時点の情報です(EmDash 0.32.0 の実装を確認しています)。