mod_rewriteの基本(サブドメイン、RフラグPTフラグ、コンテキスト)
公式ドキュメントとは違う切り口での確認をしておいたらいいかなと。細かいバリエーションについては、気が向いたら追記するかもしれません。
RフラグとPTフラグとaliasの微妙な関係
ある条件下で、fooに対するリクエストをbarとして処理したいが、別な理由でbarへのリクエストはDocRoot外で処理したいといったとき、barからpath/to/real/barへのaliasを用意しておき、fooからbarへのRewriteRuleがあれば、動作しそうです。たとえば下記のように。
RewriteRule /foo /bar Alias /bar /path/to/real/bar
ところが、これはうまく動作しません。RewriteRuleが適用された後、基本的にmod_aliasの処理が飛ばされてしまうためです。
この問題を解決する普通な方法はRフラグ、もしくはPTフラグを使うことです。Rフラグではブラウザにリダイレクトを指示するのに対してPTフラグは内部的にmod_aliasへ処理を渡します。
これはコールセンターのたらいまわしに似ています。
通常
RewriteRule /センター/故障 /修理窓口 [L]
Alias /修理窓口 /修理工場
客 - この件なんだけど
コ - それでしたら、別の窓口になりますので転送しますのでお待ちください
コ - (番号違いで繋がりません)ツーツーRフラグ
RewriteRule /センター/故障 /修理窓口 [R,L]
Alias /修理窓口 /修理工場
客 - この件なんだけど
コ - それでしたら、この番号におかけ直しください。
客 - 修理窓口にかけなおしたら、修理工場につながったよwPTフラグ
RewriteRule /センター/故障 /修理窓口 [PT]
Alias /修理窓口 /修理工場
客 - この件なんだけど
コ - それでしたら、別の窓口になりますので転送しますのでお待ちください [PT]
コ - 修理窓口の予定でしたが、修理工場に転送します。
という具合に、Rフラグは客に自分でリクエストしなおせと言っているわけです。ですので、ブラウザのアドレス欄やブラウザキャッシュに影響させたいときはRフラグを使います。外部リクエストですので、mod_aliasがそのまま生きます。その分、余計にコストがかかります。
ではPTフラグはどうかというと、通常だとmod_aliasのサーバコンテキストでの書き換えが終了すると、url-filname変換が終わっているので、mod_aliaseは仕事ができません。PTフラグを使うことでURL変換だけにして、url-filename変換をmod_aliasに任せることで、2段書き換えが可能になります。
RフラグとPTフラグの動作を理解すると、mod_rewriteの書きかえってホントは何よの部分の見通しが良くなると思います。
サーバコンテキストとディレクトリコンテキスト
mod_rewriteの振舞いが、サーバ設定ファイルやvirtualhost内に書かれた場合と、
それぞれのコンテキストにRewriteRuleを書いたとすると、概念的なフローとしては
サーバコンテキスト RewriteRule URL(リクエスト) → URL → hook_uri2file URL → Directory ディレクトリコンテキスト Rewriteの魔術 Directory→URL RewriteRule URL(rewriteBase適用後) → URL → サブリクエスト URL → Directory
ディレクトリコンテキストでmod_rewriteが行っている二つのマジックが見てとれます。また、これらマジックに付随して、DOCUMENT_ROOTやREQUEST_FILENAMEなどの環境変数の扱いが変わってきます。それぞれのコンテキストでの意味とRewriteの役割を理解していないと、微妙にはまりやすいポイントになっています。
例:サーバコンテキストではREQUEST_FILENAMEはパス化されていないのでREQUEST_URIとほぼ同値、一方ディレクトリコンテキストでは実ファイルシステム内のパスを指す。DOCUMENT_ROOTはディレクトリコンテキストでは意味をなさないとか。
ドメイン、サブドメインなどの扱い
mod_rewriteの王道とは離れるかもしれませんが、mod_rewriteのURL加工があまりに便利なので、各種調整をここでやってしまいたくなります。ここではよくあるパターンとしてURL中のホスト名を扱う方法だけメモしておきます。他の環境変数についても、アプリケーションの目的に応じて自在にコントロールできるのでアプリケーションに余計な判定を持ち込みたくないときには有効ですね。
ドメインをURLに含ませてしまう
mod_rewriteが対象にするURLにはドメインが含まれませんが、virtualhostなどで複数のサブドメインやAliasHostを使っているときにはドメイン部で書き換えたいケースがあります。最初の例では、ドメインをパスに含ませてしまいます。
# 必要に応じてRewriteCond RewriteRule ^(.+) %{HTTP_HOST}$1 [C] RewriteRule ^([^.]+)\.example\.com(.*) /path/to/$1$2 [L,QSA]
条件一致からパラメーターを拾う
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com$ [NC] RewriteRule ^(.*) /path/to/%1$1 [L,QSA]
あんまり変わりませんが、RewriteCondでのパターン一致は%1 RewriteRuleでのパターン一致は$1となるところ
環境変数にセットする
私は個人的にはこちらの方が好みなのでこちらでやってます。
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com$ [NC] RewriteRule ^ - [C,E=HOSTID:%1_example_com] ;この後に、%{ENV:HOSTID}をつかって必要に応じた処理を行う。
直接パスに変換する前に別な環境変数にセットして、%{ENV:HOSTID}といった呼び出しにして別な場所でも使えるようにしておきます。同じ変数をCGI等でそのまま流用できるので、わざわざPATHINFOやGETの解析をする必要がなくなるところも長所かと思います。また、扱うドメインが増えた時などでも、ドメイン部の処理とパスの処理を分離できるというメリットもあります。
冗長な処理が必要かどうかが使い分けるポイントかと。