【デバッグ】.htaccessでRewriteRuleの動作確認をしたい時は HTTP環境変数をクエリストリングに出力してみよう

Web制作

.htaccessて、どうも苦手なんですよね。

いつも思った通りに動いた試しがない、、、

正規表現が思った通りにマッチしなかったり意味不明な無限ループに陥ってしまうんですよね。

で、先日も例によって.htaccessで正規表現のマッチングがどうもうまく行っていないような現象に遭遇。

なんとかマッチングに使われている文字列を表示できないかなーなんて探していたら、ズバリありました。

結論から先に書くと、以下のような感じで目的とするHTTP環境変数をクエリストリングに出力することができます。

RewriteEngine on
RewriteRule ^(.*)$ /?REQUEST_FILENAME=%{REQUEST_FILENAME}&REQUEST_URI=%{REQUEST_URI}---($1) [R,END]

これで、例えばhttps://mydomain.com/admin/index.phpなんてURLにアクセスすると、ブラウザのアドレスバーにHTTP環境変数が表示されます。

実際にやってみた様子を見てみましょう。

こんな感じで対象のファイルにアクセスすると、、、

すると、ブラウザのアドレスバーに目的のHTTP環境変数が出てきます。

http://mydomain.com/?REQUEST_FILENAME=/var/www/html/test/admin/index.php&REQUEST_URI=/admin/index.php—(index.php)

これで、/admin/index.phpにアクセスしたときに.htaccessで処理されているHTTP環境変数は以下のようになっていることが分かります。

REQUEST_FILENAME→/var/www/html/test/admin/index.php
REQUEST_URI→/admin/index.php
^(.*)$でキャプチャした文字列→index.php

解説

無謀にも簡単に解説してみます。

まずは前半部分を見てみます。

RewriteRule ^(.*)$ /?REQUEST_FILENAME=%{REQUEST_FILENAME}~(省略)

この部分、RewriteRuleの基本構文は以下です。

RewriteRule 書き換え元パターン 書き換え先

そして、書き換え元パターンに^(.*)$を書いているので、全ての文字列にマッチします(つまり、どんなURLでも書き換えが発生するという意味)。

次に、書き換え先を見てみます。

/?REQUEST_FILENAME=%{REQUEST_FILENAME}

最初が/?となっています。

つまり、書き換え先を空文字とし、すぐにクエリストリングを記述に入っています。

クエリストリングについての解説はいろいろあるのですが、要はURLの最後に?に続けて「変数1=値&変数2=値」の形式で書いてやると、変数1と変数2をプログラムに渡すことができる、というものです(詳しくは自分でググってください)。

さて、これを踏まえて「?」以降を見ると、クエリストリングが

REQUEST_FILENAME=%{REQUEST_FILENAME}

となっています。

前半の「REQUEST_FILENAME=」はプログラムに渡す変数名がREQUEST_FILENAMEである事を示しています。

そして、次に%{REQUEST_FILENAME}の部分の%{***}は、HTTP環境変数を参照するための書式です。この場合はREQUEST_FILENAMEという環境変数を参照しています。

ここまでくると、以下の書き方は環境変数REQUEST_FILENAMEとREQUEST_URIをクエリストリングに書き込んでいることが分かります(複数の変数を&で区切っているだけ)。

RewriteRule ^(.*)$ /?REQUEST_FILENAME=%{REQUEST_FILENAME}&REQUEST_URI=%{REQUEST_URI}

そして、次に「—($1)」が来ています。

RewriteRule ^(.*)$ /?REQUEST_FILENAME=%{REQUEST_FILENAME}&REQUEST_URI=%{REQUEST_URI}---($1) [R,END]

ここは、あまり決まった書式はないのですが、$1という書き方だけは覚えておく必要があります。

これは、書き換え元パターンで指定した部分を、書き換え先文字列内で参照するためのものです。

と、言葉で書いても全く意味が分からないのですが、かなり乱暴な言い方をすると、書き換え元の()にマッチする部分を切り出して、書き換え先で使えるようにしてくれる、といった感じでしょうか。

イメージ的にはこんな感じでしょうか。

書き換え元のカッコ()は必ず必要です。というか、このカッコに囲まれた部分だけを、書き換え先で使うことができます。また、書き換え先でここでは—()という書き方をしましたが、これに深い意味はありません。別に「$1」と書くだけでも成立しますが、少し見やすいように—を入れた程度の意味合いです。カッコも別に必要ありません。なんとなく、キャプチャした文字列であることが分かりやすいようにカッコでかこっただけです。

上の例での結果を見ると、「index.php」となっているので、アクセス先のパスを含めないファイル名のみが入っているようです。

って、あれ?そうだったかな、、、?そんなはずないんですけど手元のテスト環境ではそうなっているようです汗

さて、予想以上に長くなってしまいましたが、最後に以下のようなオプションがついています。

RewriteRule ^(.*)$ /?REQUEST_FILENAME=%{REQUEST_FILENAME}&REQUEST_URI=%{REQUEST_URI}---($1) [R,END]

Rをつけるとアドレスバーに表示される文字列もリダイレクト先のURLに変更されます。

なのでRをつけないと、アドレスバー上で何も変化が起こらないので意味がありません。

次に、ENDをつけると、そこで.htaccessの処理を停止します。このオプションはapache 2.3以降でないと使えないようです。

注意点

この方法でHTTP環境変数を出力する場合は、いろんな注意点があります。

注意1:運用中の本番環境には入れないようにしましょう。

当たり前ですが本番環境に入れたらすべての利用者にセンシティブな情報を晒してしまうことになります。ダメ。絶対。

そんな人おらんやろと思いますが念のため。

注意2:mydomain.com直下に.htaccessがあるとうまくいかないかも

上のサンプルをそのまま適用すると、書き換え先のアクセスはmydomain.com/となります。mydomain.com/admin/ではない点に注意です。

なので、mydomain.com直下に.htaccessがあると、転送後にその.htaccessが処理されるためこの方法ではうまく表示できない可能性があります。

その場合は、書き換え先の/?の部分を、.htaccessがないフォルダに指定してあげてください。例えば、imgフォルダに.htaccessが存在しないのであれば、/img/?といった具合です。

注意3:クエリストリングの部分に***=は要らない

今回は、少し分かりやすいようにクエリストリングの部分をREQUEST_FILENAME=%{REQUEST_FILENAME}という書式でサンプルを示しました。

しかし、実際はプログラムで使うわけではないので、別に%{REQUEST_FILENAME}だけでも構いません。例えばこんな感じ。

RewriteRule ^(.*)$ /?%{REQUEST_FILENAME}&---%{REQUEST_URI}---($1) [R,END]

この場合、それぞれの変数が「—」で区切って出力されます。

むしろ、REQUEST_FILENAME=%{REQUEST_FILENAME}と書いてしまうと、書き換え先のプログラムで偶然このREQUEST_FILENAMEという名前を使っていたら、誤動作してしまう可能性があるので、あまり好ましくはないかもしれません。

注意4:RewriteRuleを書く場所に注意

既存の.htaccessがあって、そこにリダイレクトの処理を実装したい、というケースは結構あるんじゃないかと思います。

で、ここで紹介した方法は[END]オプションで.htaccessの処理を中断している点に注意が必要です。

なので、問題となっているうまく動かない場所の直前に入れるのが基本です。

しかし、問題となっている個所以降に[L]オプションがあるような場合は、必ずしも期待した結果が得られません。Lオプションの挙動は結構難しく、以下のページで詳しく紹介されていますので、一度目を通すのが良いと思います。

.htaccess に RewriteRule 書くときは、[L]フラグをつけてもそこで終了しないかもよ?って話。

まとめ

思ったより長くなりました、、、

RewriteRuleはとにかくいう事を聞いてくれないので、この方法でどんな文字列がパターンマッチに使われているのかを把握することができたので、とても重宝しました。

もし、運用中の本番環境で同じようなことをしたい場合には、アクセス元のURLで処理を絞るなどの応用が必要かと思います。

コメント

タイトルとURLをコピーしました