WordPressやその他のCMS、フレームワークを採用している時に.htaccess
に記述したmod_rewrite
が効いてくれなくて、URLの書き換えができない…そんな時、フレームワークに採用されている「ルーティング機能」のためのルールセットと、独自のルールセットの順番を入れ替えてみるとサクッと解決することがあります。
ルーティング用の設定とは?
WordPressだけでなく多くのフレームワークには、リクエストされたURLに対象となる実体(Webページや画像など)が存在しない場合に、システム本体に要求されたURLであると認識し、リクエストされたURLの解析を行って適切な処理を実行する機能があります。
- リクエストされたURLに対象となる実体(Webページや画像など)が存在しない場合に、
- リクエストされたURLの解析を行って適切な処理を実行する
この後半「2」の機能が「URLルーティング」で、WordPressでは/index.php
が「URLルーター」としてリクエストされたURLを解析し、適切なコントローラーを呼び出すことで処理が実行されページが表示される、という仕組みになっています。
で、前半「1」の「リクエストされたURLに対象となる実体が存在しない場合に」そのルーターにリクエストを集める役割を担っているのが.htaccess
に書かれたルーティング用の設定=mod_rewrite
の次のルールセットです。
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
5行目は/index.php
が直接呼ばれた場合の処理で、何もせずリライトを停止させます。
6行目7行目が「該当するファイルやディレクトリの実体がない場合は」という書き換えの条件で、該当する場合その下8行目「全て.
を/index.php
に書き換える」という処理が実行されます。
つまり、実体のないURLへのリクエストは、すべてルートにある/index.php
に渡すと指定しているのです。(これが「WordPressはデフォルトだと404ページが表示されない」理由です)
基本的なmod_rewriteの処理の流れ
ここでmod_rewrite
の処理の流れに関するルールをおさらいします。
mod_rewrite
は上から順番に処理が実行されるRewriteRule
のフラグに[R]
や[L]
が付与された場合、条件にマッチした時点で書き換え処理を停止し、それ以降の書き換えは行わない。(以降のルールセットは評価されない)
よって、指定する順番が挙動に影響します。
ルールセットの指定順に注意
ルーティング用のルールセットはRewriteRule
のフラグに[L]
が指定されているので、条件にマッチして書き換え処理が実行された場合、それより後のルールセットはスルーされます。
「実体がないURL」の「すべてを」という条件はかなり対象が広いので、これが一連のリライト処理の初めの方に指定されると、本来処理してほしいルールセットに行きつくまでに処理が停止されて、意図した結果が得られない、ということが起こります。
例1)動的URLを静的化するためのルールセット
パラメータ付きの動的なURLを静的化するため、次のようなルールセットを用意したとします。
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^products/detail-([0-9a-zA-Z_-]+).html?$ products/detail.php?product_id=$1 [L,QSA]
この場合「ファイルやディレクトリの実体がないURLの場合は」という書き換えの条件はルーティングの設定と同じで、違いは、対象が「リクエストされたパスがproducts/detail-**
で始まる」場合に限定されているところです。ルーティングの設定は「全て.
」が対象なので、より詳細度の高いこのルールセットが上位にないとルールは実行されません。
例2)強制的にステータスコード404を返すためのルールセット
何らかの理由で、特定のURLに対し強制的にステータスコード404を返すため、次のようなルールセットを用意したとします。
RewriteCond %{REQUEST_URI} ^/payment.html$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/buy.html$ [NC,OR]
RewriteCond %{REQUEST_URI} ^/searches/.*$ [NC]
RewriteRule ^.*$ - [R=404,L]
この場合も同様で、強制的に404を返したいURLは実体がないはずです。よってこれよりルーティング用の設定が先にある場合、そちらで書き換えが処理されてしまうので、このルールセットが実行されることはずっとないのです。
ルーティング用の設定は一番最後に
「実体がないURL」の「すべて」より緩い条件でURLの書き換えが必要になるケースはありません。なのでルーティング用の設定は、mod_rewrite
ブロックの一番最後に記述するようにしましょう!
既にWordPressが使用されているサイトに手を加える場合など、ついついルーティングの設定より後に追記する形でmod_rewrite
のルールセットを追加して「動かない!」ってなっているのを見かけたり、「URLの正規化や静的化がうまくいってないからちょっと見て欲しい」と言われて覗くと、同様になっているケースがよくあるので、「おや?」と思ったら確認してみてください。