旧URLの受け皿はCMSの中に置く — ヘッドレスCMSのリダイレクトと404ログの実務
リニューアルで作った301の表は、公開した瞬間から古くなります。旧URLの受け皿をサーバー・エッジ・CMSのどこに置くか、EmDashの実装(スラッグ変更時の自動301・パターン・410)と、自サイトの404ログ1,211件の読み方を整理しました。

「旧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を変えた本人が、同じ管理画面で受け皿も作れる。

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というグループ名が付く

下書きの段階でスラッグを変えた場合は、公開したタイミングで入ります。公開までは旧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件から十数件に減ります。

その12件を眺めていて、自分の実装の粗も見つかりました。このサイトは存在しない記事URLを Astro.redirect("/404") で404ページへ飛ばしています。302が1回挟まるので、ログに残るのは /404 という行だけ。実際に叩かれたURLが消えてしまう。参照元付き12件のうち最多だったのが、この /404 でした。存在しないURLは飛ばさず、その場で404を返す。ここは直します。
リダイレクトは、公開前に一度作って終わる作業ではありません。URLを変える人と、受け皿を作る人と、結果を見る場所が離れているほど漏れます。
移行やリニューアルで「旧URLの扱いをどう設計するか」から相談したい方は、お問い合わせからどうぞ。いまのサイトに404がどう溜まっているかを見るところからお手伝いします。
本記事は2026年9月時点の情報です(EmDash 0.32.0 の実装を確認しています)。