【Xserver×Astro移行の罠】なぜ404ではなく500無限ループになるのか?親WordPress『.htaccess継承・日本語URL後方参照バグ・XPageSpeed』完全解決録
長年運営した WordPress ブログから Astro などの高速な静的サイト(SSG)へ移行する際、レンタルサーバー(エックスサーバー等)のサブドメイン環境で思わぬ地雷を踏むことがあります。Google Search Console を開いたところ、「サーバーエラー (5xx) が 276 件」「?PageSpeed=noscript 付きの重複 URL が 400 件以上」 という大量の警告が出てヒヤリとした実体験がその典型でした。
.htaccess 継承による 404 → 500 内部転送ループ」「② Apache mod_rewrite で日本語 URL の %20 等を書くと %2 が後方参照として消滅する仕様罠」「③ Xserver の『XPageSpeed』自動挿入リンクと WebP 画像 1 秒キャッシュの盲点」の 3 つでした。本記事では、これらを一掃して爆速キャッシュと完璧な 301 正規化を両立させた .htaccess 設計を公開します。 -
【Web安全設計の基本】なぜ古いPHPサイトやC言語ソフトは狙われるのか?『メモリの番地(ポインタ)手作業管理』の限界とRust・静的サイトが安全な理由 : そもそもなぜ WordPress(PHP+DB)から Astro 静的サイトへ移行するとセキュリティと速度が劇的に向上するのかを解説しています。
-
【WordPress70記事→Astro完全移行録】Cocoon・カエレバ・KATTENEと広告ブロッカー消失罠を突破する『AffiliateCard』と11大共通ルール : サーバー設定とセットで行った全記事・画像 1,625 枚・商品リンク枠 185 個の移行実践録です。
-
【SEO×AI検索の両取り戦略】Astroで『llms.txt・ai-corpus.jsonl・citation構造化データ』を自動生成してAIに引用されるサイトを作る実装ガイド :
.htaccessで即時配信に設定した AI 専用コーパスと構造化データの自動生成ガイドです。
-
エックスサーバー(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...)して右辺に書こう!」と思って以下のように書くと、文字が無残に壊れます。
-
NG①:右辺に
%20や%E7を直接書く(%N後方参照消失バグ) :mod_rewriteの置換文字列(右辺)では、%0〜%9が 「直前のRewriteCondのキャプチャ後方参照」 という特別な意味を持ちます。そのため%20(半角スペース)と書くと、%2が「空っぽの後方参照」として消滅してただの0に化け、URL が破壊されます! -
NG②:それを防ごうと
\%20や\%E7とバックスラッシュでエスケープする : 環境や記述位置によっては不正エスケープと判定され、サイト全体が即座に500 Internal Server Error(構文エラー)で落ちます。また左辺の正規表現で全角コロン等を[::]のようにバイト単位の文字クラス[...]に入れると UTF-8 の途中バイトに誤マッチするため、必ず(?::|:)とグループ化して書きます。 -
✨ 正解:右辺は『ダブルクォート囲みの生 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・JSON | access 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 静的サイトは驚くほど堅牢かつ爆速に動いてくれます。
- サブディレクトリ運用の子サイトには必ず独自の
RewriteEngine OnとErrorDocument 404を置き、親 WordPress のルール継承による 500 ループを遮断する mod_rewriteで日本語 URL へ転送する際は%20等を手書きせず、ダブルクォート囲み生 UTF-8 +[R=301,L]でサーバー標準エスケープに任せる- 静的サイトでは「XPageSpeed」をサーバーパネルから OFF にし、
mod_expiresで「HTML は 0 秒即時反映・WebP や CSS は 1 年キャッシュ」へ最適化する
移行後の Search Console エラーでお困りの方は、ぜひ一度 .htaccess と通信ヘッダーを点検してみてください!

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