【Xserver×Astro移行の罠】なぜ404ではなく500無限ループになるのか?親WordPress『.htaccess継承・日本語URL後方参照バグ・XPageSpeed』完全解決録
作成日:
更新日:

【Xserver×Astro移行の罠】なぜ404ではなく500無限ループになるのか?親WordPress『.htaccess継承・日本語URL後方参照バグ・XPageSpeed』完全解決録


🔧 論旨

長年運営した WordPress ブログから Astro などの高速な静的サイト(SSG)へ移行する際、レンタルサーバー(エックスサーバー等)のサブドメイン環境で思わぬ地雷を踏むことがあります。Google Search Console を開いたところ、「サーバーエラー (5xx) が 276 件」「?PageSpeed=noscript 付きの重複 URL が 400 件以上」 という大量の警告が出てヒヤリとした実体験がその典型でした。

調査の結果、真犯人は Astro ではなく「① 親ディレクトリの WordPress 用 .htaccess 継承による 404 → 500 内部転送ループ」「② Apache mod_rewrite で日本語 URL の %20 等を書くと %2 が後方参照として消滅する仕様罠」「③ Xserver の『XPageSpeed』自動挿入リンクと WebP 画像 1 秒キャッシュの盲点」の 3 つでした。本記事では、これらを一掃して爆速キャッシュと完璧な 301 正規化を両立させた .htaccess 設計を公開します。
🔧 合わせて読みたい深まる関連情報
🔧 対象となる方
  • エックスサーバー(Xserver)等のレンタルサーバーで、親ドメインに WordPress を残したままサブドメイン(サブディレクトリ)で静的サイトを運用している方

  • 存在しない URL にアクセスした際、素直に 404 Not Found にならず 500 Internal Server Error が大量発生して Search Console で警告が出ている方

  • .htaccess で日本語カテゴリー名やスペース入り URL へ 301 リダイレクトを書いたら、文字化けや 500 構文エラーにハマってしまった方

  • Xserver の 「XPageSpeed」を切るべきか迷っている方、および切った後のブラウザキャッシュ最適化 を知りたい方

吹出しアイコン

ひえええっ、大変じゃ!!新しく作った静的サイト自体はサクサク表示されとるのに、Google Search Console を見たら「サーバーエラー (5xx)」が何百件も出とるぞ!PHP もデータベースも置いとらんのに、なんで 500 エラーなんて出るんじゃ!?

吹出しアイコン

おじさん、慌てなくて大丈夫ですよ!サイト自体が壊れているわけではなく、実はレンタルサーバー特有の 「フォルダ階層と .htaccess の親子継承ルール」 が悪さをしているんです。原因が分かれば .htaccess ファイル 1 枚でピタッと根治できますよ!


存在しないURLが404ではなく500になる怪現象

親WordPressの転送ルール継承という盲点

まず最初に解明したのは、「なぜ削除した古いページや打ち間違いの URL にアクセスした時、通常の 404 Not Found ではなく 500 Internal Server Error が返っていたのか?」という謎です。

原因は、レンタルサーバーにおけるサブドメインの物理フォルダ配置にありました。たとえば親ドメイン(example.com)の直下で従来の WordPress が動いており、新しく作ったサブドメイン(lab.example.com 等)の公開フォルダがその配下のサブディレクトリ(public_html/lab.example.com/)に作られているケースです。

このとき、親フォルダ(public_html/.htaccess)には WordPress 定番の次の転送ルールが書かれています。

# 親ディレクトリ(WordPress側)にある定番ルール
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]

もし子ディレクトリ(Astro 静的サイト側)に独自の RewriteEngine On を持たないまま放置すると、Apache の仕様によって 親ディレクトリの RewriteRule . /index.php [L] が子サイトにも継承 されます。すると、子サイト上で存在しない URL が叩かれた瞬間、「ファイルが無いなら /index.php へ内部転送せよ! → しかし静的サイトだから index.php なんて存在しない! → 存在しないからまた /index.php へ転送せよ!」という 無限内部リダイレクト(Internal Recursion Limit 超過) が発生し、Apache が強制ブレーキをかけて 500 Internal Server Error を吐き出していたのです。

子ディレクトリの.htaccessでループを断つ方法

解決策は驚くほどシンプルです。Astro プロジェクトの public/.htaccess(ビルド時にそのまま公開ルート直下へ出力されるファイル)に RewriteEngine On と ErrorDocument 404 /404.html を明記し、src/pages/404.astro で案内用のカスタム 404 ページを作っておくだけです。

Apache では、子ディレクトリ側で RewriteEngine On を宣言すると(RewriteOptions Inherit を明示しない限り)親ディレクトリの RewriteRule 継承がそこで完全に遮断 されます。これだけで、存在しない URL には正しく HTTP ステータス 404 が返り、500 ループは瞬時に消滅します。


mod_rewrite日本語リダイレクトの2大トラップ

%20の%2が消滅する後方参照バグと解決の鉄則

500 エラーを止めたら、次は旧記事スラッグや表記揺れしていた日本語カテゴリー URL(例:第01部:... → 第 1 部:...)を正規 URL へ 301 リダイレクトさせて検索評価を引き継ぎます。ところが、ここで多くのエンジニアが一度は徹夜する Apache mod_rewrite 最大の罠 に遭遇しました。

「URL に日本語や半角スペースが含まれるから、丁寧にパーセントエンコード(%20 や %E7%AC%AC...)して右辺に書こう!」と思って以下のように書くと、文字が無残に壊れます。

🔧 💣 Apache『mod_rewrite』で日本語URLを扱う際の3つのNGと正解
  1. NG①:右辺に %20 や %E7 を直接書く(%N 後方参照消失バグ) : mod_rewrite の置換文字列(右辺)では、%0 〜 %9 が 「直前の RewriteCond のキャプチャ後方参照」 という特別な意味を持ちます。そのため %20(半角スペース)と書くと、%2 が「空っぽの後方参照」として消滅してただの 0 に化け、URL が破壊されます!

  2. NG②:それを防ごうと \%20 や \%E7 とバックスラッシュでエスケープする : 環境や記述位置によっては不正エスケープと判定され、サイト全体が即座に 500 Internal Server Error(構文エラー)で落ちます。また左辺の正規表現で全角コロン等を [::] のようにバイト単位の文字クラス [...] に入れると UTF-8 の途中バイトに誤マッチするため、必ず (?::|:) とグループ化して書きます。

  3. ✨ 正解:右辺は『ダブルクォート囲みの生 UTF-8 文字列 + [R=301,L](NE フラグなし)』で書く : 置換先を "..." で囲んで日本語のまま書き、あえて NE(No Escape)フラグを 付けずに [R=301,L] とだけ指定します。すると Apache 内部の ap_escape_uri 関数が RFC 準拠の正確なパーセントエンコードを自動で行い、100% 確実に 200 OK で着地します。

# 正解例:日本語と半角スペースを含む正規カテゴリーURLへの安全な301リダイレクト
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^categories/第\ *0?1\ *部(?::|:).*$ "/categories/第 1 部:深掘り分析室/" [R=301,L]

ソフト404を防ぐ移転先ファイルの実在確認条件

さらに、ディレクトリ構造を整理した際(たとえば /blog/review/<slug>/ から /blog/review-old/<slug>/ へ過去記事を移動した際)、無条件にワイルドカードで転送してしまうと、「最初から存在しないデタラメな URL」まで移転先へ 301 転送された挙げ句に 404 になる(2 段ホップの悪質リダイレクトやソフト 404 判定) を招きます。

これを防ぐため、自動リダイレクトルールには必ず RewriteCond %{DOCUMENT_ROOT}/移転先/$1/index.html -f(移転先に本当に HTML 実体ファイルが存在する場合のみ 301 転送する) という実在ガード条件を付けました。

# 移転先に実体ファイル(index.html)が存在する場合のみ 301 転送し、無ければ素直に 404 へ落とす
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/blog/review-old/$1/index.html -f
RewriteRule ^blog/review/([a-zA-Z0-9_-]+)/?$ /blog/review-old/$1/ [R=301,L]

XPageSpeed解除とWebP1年キャッシュ最適化

重複URLを量産するXPageSpeedの落とし穴

続いて Search Console を汚していた 2 つ目の黒幕が、末尾に ?PageSpeed=noscript が付いた 400 件超の重複 URL でした。

これはエックスサーバーの高速化機能 「XPageSpeed」 を ON にしていると、サーバー側が配信時の全 HTML を自動書き換えし、<noscript> タグ内に ?PageSpeed=noscript 付きのリンクを勝手に埋め込むために発生します。すでに Astro 側で全画像を軽量な WebP 化し CSS も最小化している静的サイトにとって、XPageSpeed の HTML 書き換えは百害あって一利なしです。

ここで 絶対にやってはいけない注意点 があります。ネット上の古い記事を見て .htaccess に ModPagespeed Off と書き込むと、エックスサーバーでは許可されていないディレクティブのため 全ページが一瞬で 500 エラー になります!必ず エックスサーバーの「サーバーパネル」→「XPageSpeed設定」から OFF に切り替えてください。

そして、過去にクローラーが覚えてしまった ?PageSpeed=... 付き URL へのアクセスは、日本語パスでも文字化け(二重エンコード)しないよう %{THE_REQUEST} からエンコード済みパスだけを抜き出して正規 URL へ 301 統合します。

# 旧 XPageSpeed が生成した ?PageSpeed=... 付きURLを二重エンコードせず正規URLへ 301 統合
RewriteCond %{QUERY_STRING} (?:^|&)PageSpeed=[^&]* [NC]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ (/[^\ ?]*)\?
RewriteRule ^ %1? [R=301,L,NE]

WebP1秒キャッシュ問題とnoindex競合の解消

吹出しアイコン

ちょっと待って!XPageSpeed を全部 OFF にしちゃったら、今度は画像のブラウザキャッシュまで効かなくなって表示が遅くなったりしないの!?

吹出しアイコン

鋭い質問です!実際に OFF にした直後の通信ヘッダーを計測してみたら、フロントエンドの Nginx(Xアクセラレータ)と Brotli 圧縮はそのまま効いているものの、標準状態のままだと WebP 画像だけキャッシュ有効期限が max-age=1(たった 1 秒!)になっていた んです!そこで .htaccess に Astro 専用のキャッシュ設定を追加しました。

Astro でビルドした画像や CSS・JS には /_astro/hero.BE5xhk39.webp のように 中身が変わると自動で変わるハッシュ値 が付きます。そのため、アセット類は思い切って 「1 年間キャッシュ(1 year)」 にし、逆に記事本文の HTML やサイトマップ・llms.txt はデプロイした瞬間にタイムラグゼロで切り替わるよう 「0 秒キャッシュ(0 seconds)」 に設定するのが最強の組み合わせです。

ファイル種別mod_expires 設定値狙いと効果
HTML・XML・TXT・JSONaccess plus 0 seconds記事を修正・公開した瞬間に、古いキャッシュが残らず 0 秒で最新内容が読者と AI に届く。
WebP画像・CSS・JS・フォントaccess plus 1 yearハッシュ付きファイルのため差し替え事故ゼロで 1 年間ブラウザキャッシュが効き、2 ページ目以降の遷移が瞬時に終わる。

最後に、Search Console 周りでもう 1 つ大切な仕上げを行いました。タグ一覧ページ(/tags/)など HTML 側に <meta name="robots" content="noindex, follow"> を入れているページに対して、robots.txt で Disallow: /tags/ を書いてブロックしてしまうと、Googlebot がページを開けず noindex タグを読み取れないため、いつまでも警告リストに残り続ける という競合が起きます。noindex を見せるページと、.htaccess で 301 正規化を通したい URL は、robots.txt で塞がずクローラーを通して正しく結果を伝えるのが鉄則です。


まとめ:レンタルサーバーの癖を知れば静的配信は無敵

専用クラウドではなく使い慣れた国内レンタルサーバー(Xserver 等)であっても、裏側の Apache とフロントエンドキャッシュの癖さえ正しく押さえれば、Astro 静的サイトは驚くほど堅牢かつ爆速に動いてくれます。

  1. サブディレクトリ運用の子サイトには必ず独自の RewriteEngine On と ErrorDocument 404 を置き、親 WordPress のルール継承による 500 ループを遮断する
  2. mod_rewrite で日本語 URL へ転送する際は %20 等を手書きせず、ダブルクォート囲み生 UTF-8 + [R=301,L] でサーバー標準エスケープに任せる
  3. 静的サイトでは「XPageSpeed」をサーバーパネルから OFF にし、mod_expires で「HTML は 0 秒即時反映・WebP や CSS は 1 年キャッシュ」へ最適化する

移行後の Search Console エラーでお困りの方は、ぜひ一度 .htaccess と通信ヘッダーを点検してみてください!

すっきり晴れた窓辺で真っ直ぐ前を見つめるノルウェージャンフォレストキャット

▲ 通り道の詰まりや回り道をきれいに掃除してあげれば、ページも気持ちよく一直線に届きます