WordPressのURLは https://example.com/blog/some-post-title/ のような「きれいな」形をしているが、サーバー上に blog/some-post-title/ というディレクトリやファイルが実在するわけではない。裏側では Apache の mod_rewrite というモジュールが、実際には存在しないパスへのリクエストを受け取り、内部的に index.php へ転送している。この仕組みを設定しているのが .htaccess ファイルの中身であり、WordPressサイトの保守で「ページが急に404や500を返すようになった」というトラブルに当たったとき、原因の多くはここに書かれたルールの解釈違いにある。
補足:
.htaccessは Apache サーバーがディレクトリ単位で読み込む設定ファイル。サーバー全体の設定ファイルを直接編集できない共有レンタルサーバー環境(Xserver・さくらインターネット等)でも、このファイルを置くだけでそのディレクトリ以下の挙動を変更できるため、レンタルサーバーで最も触る機会の多い設定ファイルの一つになっている。
基本構文 — 4つの要素
WordPressが標準で書き出す .htaccess は、次の4要素の組み合わせでできている。
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]
</IfModule>
# END WordPress
RewriteEngine On— このディレクトリ以下で書き換え機能を有効にする宣言。これがないと以下の行は全て無視されるRewriteCond(条件)— 直後のRewriteRuleを適用する「前提条件」を書く。%{REQUEST_FILENAME} !-fは「リクエストされたパスが実在するファイルではない場合」、!-dは「実在するディレクトリではない場合」という意味。この2条件が両方成立したときだけ、次のRewriteRuleが発動するRewriteRule(書き換え規則)—パターン 置換先 [フラグ]の3要素。.は「1文字以上の任意の文字列(=ほぼ全てのパス)」にマッチするパターンで、それを/blog/index.phpに内部転送する、というのがWordPressのパーマリンク機構の正体[L]フラグ — “Last” の略。このルールが適用されたら、それ以降のルールを評価せず処理を打ち切る。複数のルールが連鎖して意図しない二重書き換えが起きるのを防ぐ
他によく使うフラグとして、[R=301](恒久リダイレクトとしてブラウザに返す・URLバーの表示も変わる)、[NC](大文字小文字を区別しない)、[QSA](元のクエリ文字列を保持したまま付け足す)がある。単なる内部転送(URLバーの表示は変わらない)と、[R=301] 付きの明示的リダイレクト(URLバーの表示が変わる)は似て見えるが全く別の動作という点は区別して覚えておく価値がある。
RewriteBase — 「相対パスの基準点」という役割
上記の中で最も見落とされやすいのが RewriteBase /blog/ の行である。RewriteRule の置換先に相対パス(先頭が / でないパス)を書いた場合、Apache はこの RewriteBase の値を基準にパスを組み立てる。WordPressインストーラーは、そのWordPressが実際にどのURLパスで動くか(home_url の値)を見て、このパスを自動生成する。通常は正しく動くが、サーバー構成をインストール後に変更した場合、この値だけが古いまま取り残されることがある。
実例 — サブドメイン化で実際に踏んだ罠
このブログの英語版(en.wpmm.jp/blog/)を立ち上げたときに、まさにこの罠を踏んだ。経緯はこうだった。
- 当初、レンタルサーバーの管理画面から
wpmm.jp/public_html/en/blog/というパスにWordPressを簡単インストールした。この時点でのURL構造はwpmm.jp/en/blog/(メインドメイン配下のサブディレクトリ)だった - その後、
en.wpmm.jpというサブドメインを作成し、docroot を同じwpmm.jp/public_html/en/に向け直した。これにより見た目のURLはen.wpmm.jp/blog/に変わる wp option update home/wp option update siteurlでWordPress内部のURL設定は新しいサブドメインのものに更新した- しかし
.htaccessのRewriteBaseは、手順1の時点の値(/en/blog/)のまま残っていた
結果として起きた症状が興味深かった。トップページ(index.php への直接アクセス)は正常に表示されるのに、個別記事・カテゴリ一覧・検索結果・404ページはすべて500エラーになった。トップページは mod_rewrite をほぼ経由せずファイルへの直接アクセスに近い形で応答できるが、パーマリンク付きの個別URLは必ず RewriteRule の内部転送を通る。そのとき古い RewriteBase /en/blog/ を基準に組み立てられたパスが、実際のドキュメントルート構造と食い違い、WordPress側が正しいURLとして認識できずにエラーを返していた。
もう一つ引っかかった点として、wp rewrite flush --hard コマンド(WordPressの内部ルーティング設定を再生成し .htaccess にも書き戻すはずのコマンド)を実行しても、このファイルは書き換わらなかった。レンタルサーバー側のパーミッション制約により、”Regenerating a .htaccess file requires special configuration” という警告だけが返り、実ファイルは変更されなかったのである。最終的な対処は、.htaccess を手動でバックアップした上で RewriteBase を正しいパス(/blog/)に書き換える、という単純だが手作業でしか解決できない方法だった。
一般化できる教訓
この経験から得られる教訓は、mod_rewriteに限らず「設定ファイルの自動生成」全般に言えることだと思う。インストール時に自動生成された設定は、生成された瞬間の前提条件を固定的に写し取ったものであり、その後インフラ構成が変わっても自動追従しない。 WordPressの wp rewrite flush のような「再生成コマンド」があっても、実行環境の権限次第では実ファイルまで届かないことがある。サブドメイン化・ディレクトリ移動・ドメイン変更のようなインフラ再構成を行った際は、コマンドの実行結果を鵜呑みにせず、.htaccess の中身を直接開いて RewriteBase の値が実際のパス構造と一致しているかを目視で確認する一手間が、遠回りに見えて最も確実な検証手段になる。
まとめ
| 要素 | 役割 |
|---|---|
RewriteEngine On |
このディレクトリで書き換え機能を有効化する宣言 |
RewriteCond |
直後の RewriteRule を適用する条件(例: 実ファイル/ディレクトリでない場合のみ) |
RewriteRule |
パターンにマッチしたリクエストを別のパスへ内部転送または外部リダイレクト |
RewriteBase |
相対パスの置換先を解決する基準ディレクトリ。インストール時の前提を固定的に反映する |
[L] |
このルールを最後に処理を打ち切る |
[R=301] |
内部転送ではなく、ブラウザのURLバー表示も変わる恒久リダイレクトとして返す |
.htaccess の中身は一見呪文のように見えるが、4つの要素の役割さえ押さえれば読み解ける。特に RewriteBase は、インフラ構成の変更時に取り残されやすい値として、意識して確認する習慣をつけておく価値がある。