Cloudflare Pages の _redirects に www リダイレクトを書いたら黙って無視された
本記事にはアフィリエイト広告(PR)が含まれます。
このサイトは shikumilog.com で公開している。ところが www.shikumilog.com にアクセスしても、同じ中身が 200 で返ってくることに気づいた。同一内容が 2 つの URL で配信されている状態だ。
致命傷ではない。www 側のページも canonical は apex(www なしのドメイン)を指しているので、検索エンジンに「正はこちら」という表明はできている。ただ、このサイトは 2026-08-09 に公開して 08-12 に Search Console へ登録したばかりの新規ドメインで、この時点(08-16)のインデックス登録は 0 件だった。その段階で、同じ内容を 2 つの URL で取りに来させるのは損だ。www → apex の 301 リダイレクトを入れることにした。
結論を先に書く。最初に入れた設定は、正しくデプロイされたのに効かなかった。原因は書き間違いではなく、書く場所——レイヤ——の間違いだった。
_redirects に書いてデプロイした
Cloudflare Pages には _redirects というファイルでリダイレクトを定義する仕組みがある。ビルド出力にこのファイルを置くと、Pages がそれを読んでリダイレクトを適用する。
私はここに www → apex の転送を書いた。ホスト名から始まる、別のホスティングサービス(Netlify)のドキュメントで見かけたことのある形だ。サイトジェネレーターのビルド出力に _redirects を吐く実装を足して、sitemap の URL エンコード修正(こちらは別の不具合で、<loc> に半角スペースがそのまま入っていた)と合わせて 1 つの PR にした。CI は通り、マージしてデプロイされた。
効かない。しかしファイルは配信されている
デプロイ後、www にアクセスすると 200 のままだった。反映待ちかと思い、20 秒間隔で 6 回計測した。6 回とも 200。301 は一度も返らなかった。
ここからの切り分けで、可能性が 1 つずつ消えていった。
まず「デプロイに失敗した」の線。同じ PR に入れた sitemap のエンコード修正は、本番の sitemap.xml で効いていた。エンコード済みの <loc> が 33 件、生の空白を含む <loc> は 0 件。デプロイそのものは成功している。
次に「ファイルが出力に含まれていない」の線。/_redirects に直接アクセスすると、トップページの HTML が返ってきた。公式ドキュメントによれば、このファイルは静的ファイルとしては配信されず、Pages に解釈される("This file will not itself be served as a static asset, but will instead be parsed by Cloudflare Pages")。つまりこの応答はむしろ「ファイルは取り込まれている」ことを示す。
つまり、設定は入っている。入っているのに、効いていない。
公式ドキュメントに「❌」があった
答えは公式ドキュメントの対応表にあった。Cloudflare Pages の _redirects の仕様には「Advanced redirects」という表があり、そこにこうある。
Domain-level redirects — ❌
例として挙げられているのが workers.example.com/* workers.example.com/blog/:splat 301、まさに私が書いた形だ。ホスト名から始まるリダイレクトは、この仕組みの対象外だった。仕様の定義でも source は「A file path.」と明記されている。ワイルドカードやプレースホルダは使えるが、いずれもパスの中の話で、ホストは最初から守備範囲にない。
そして、今回いちばん効いた一文がこれだ。
Only one redirect can be defined per line and must follow this format, otherwise it will be ignored.
形式に合わない行は、エラーにならない。警告も出ない。黙って無視される。 CI は通る。デプロイは成功する。ファイルは取り込まれる。それでも、その行だけが存在しないものとして扱われる。「設定は入っているのに効かない」ように見えたものの正体は、「入ってはいるが、読まれていない」だった。
私の側の誤りは単純で、Netlify の _redirects とファイル名が同じだから中身も同じだろう、と確認せずに書いたことだ。同じ表を見ると、クエリパラメータによるマッチや国・Cookie による出し分けも ❌ で、ホスト指定はその一例にすぎない。ファイル名が同じでも、別の製品では別物だった。なお、この書き方が Netlify 側で本当に効くのかは、今回は検証していない。
ホストの転送は CDN の仕事だった
ホスト単位のリダイレクトは、ビルド出力ではなく CDN 側の設定でやる。Cloudflare にはダッシュボードに Redirect Rules があり、「WWW からルートにリダイレクト」というテンプレートまで用意されていた。
設定した内容はこうだ。
- 条件: Hostname equals
www.shikumilog.com - 転送先:
concat("https://shikumilog.com", http.request.uri.path)への Dynamic redirect - ステータスコード: 301、クエリ文字列は保持
設定後の実測。トップ・記事 URL・クエリ付き URL の 3 パターンで確認し、3 パターンとも apex へ 301 が返った。パスとクエリは保持され、apex 側は 200 のままでリダイレクトループもない。
効かなかった _redirects の出力は、別の PR で削除した。動かない設定をリポジトリに残すと、後からコードを読んだ人間——たいてい未来の自分——が「www の転送は対応済み」と誤読するからだ。効かないと分かった設定は、消すところまでが後始末だと思う。
同じことをする人へ
- Cloudflare Pages の
_redirectsで書けるのはパスのリダイレクトだけ。www → apex のようなホスト単位の転送は公式が非対応(Domain-level redirects: ❌)と明記している - 形式に合わない行はエラーも警告もなく無視される。CI やデプロイの成功は、その行が機能していることを何も保証しない
- 「設定が効かない」ときは、設定の中身を疑う前にレイヤを疑う。ホストの転送は CDN(Redirect Rules)の仕事で、ビルド出力の仕事ではなかった
- 切り分けは「同じデプロイで効いたものがあるか」から始めると速い。私の場合、同じ PR の sitemap 修正が効いていたことで、デプロイ失敗の線が最初に消えた
- ファイル名が同じ設定ファイルでも、製品が違えば仕様は別物。持ち込む前に持ち込み先のドキュメントを読む。私はこれを怠って、公式の表を見れば分かることに実測 6 回分の回り道をした
この記事は、運営者自身の実践記録に基づいています。
出典
- Cloudflare Docs - Redirects · Cloudflare Pages(2026-08-16 閲覧)