Gマイナー志向

とくに意味はありません

ApacheのRewriteRuleで検索条件に^/がマッチしない場合がある理由

ApacheのRewriteRuleで「/hoge/配下にアクセスがあったら http://example.jp/fuga/ に301リダイレクトを行う」ってな処理を書く場合、先頭に/が必要な場合と必要ない場合がある。

RewriteEngine on
# これでいける場合もある
RewriteRule ^/hoge/(.*) http://example.jp/fuga/$1 [R=301,L]
# こう書かないとダメな場合もある
RewriteRule ^hoge/(.*) http://example.jp/fuga/$1 [R=301,L]

この先頭に/が必要不要の違いはなんだろうなーと思っていたが、先日理由がわかった。
Apacheのドキュメントにしっかり書いてあった。

RewriteRule Directive

What is matched?

  • In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. "/app1/index.html").
  • In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that led the server to the current RewriteRule (e.g. "app1/index.html" or "index.html" depending on where the directives are defined).

つまりざっくり説明するとこんな感じか。

  • VirtualHostディレクティブの場合はURLのパスが対象となり先頭に/がつく
  • Directoryディレクティブや.htaccessの場合はそのディレクトリからの相対ファイルパスが対象になり先頭に/がつかない

知らなかった。そういうことだったのね。
ちなみにVirtualHost内にDirectoryディレクティブの中でRewriteRuleを書いた場合は後者になった。

どこに書いても使えるパターン

上記を意識して書いてもいいのだが、できればポータビリティを考慮してどこに書いても使えるように書きたい。
じゃあどう書くか。私はこんな感じで書いてる。

RewriteEngine on
RewriteRule ^/?hoge/(.*) http://example.jp/fuga/$1 [R=301,L]

?を入れてどちらでもマッチするように。
さらにエンコードやQueryStringもきちんと引き継ぐようにするなら、こんな感じで。

RewriteEngine on
RewriteRule ^/?hoge/(.*) http://example.jp/fuga/$1 [R=301,L,QSA,NE]

これはすべてのパスを別サイトに転送する場合も同様に書ける。

RewriteEngine on
RewriteRule ^/?(.*) http://example.jp/$1 [R=301,L,QSA,NE]

先頭の/を除外するおかげて転送先URLの記述が美しいですね。