Table of Contents
mod_rewrite活用テクニック
- 出典
- Software Design 2007年 09月号
- テスト環境
- CentOS5 テスト用最小インストール手順参照。
- apacheバージョン
- 2.2系
mod_rewriteとは
- URL書き換えモジュール。
- 正規表現で書き換え
正規表現いろいろ
- この辺りを参考に。
- ? の使い方
直前にある部分正規表現、もしくは空文字列にマッチします。
すなわちhoohoohoo
の最初の"oo"を取りたい場合は、h(.+)hoo
だと、"oohoo"になる。こうではなくて、h(.+?)oo
になりまっす。知らなんだ。
mod_rewriteの導入
ビルド&インストール
CentOS5の標準パッケージでは動的に組み込まれている。
# httpd -M 2>&1 | grep -i rewrite rewrite_module (shared)
ソースからビルドした場合はapxsを使う。
# cd /usr/local/src/httpd-2.2.4/ # /(apxsへのパス)/apxs -i -a -c modules/mappers/mod_rewrite.c
httpd.confでロードされていることを確認。
LoadModule rewrite_module modules/mod_rewrite.so
設定
基本
RewriteEngine On RewriteCond 〜 RewriteRule 〜
- RewriteEngine
-
mod_rewriteの有効化/無効化。例えば/var/www/html/fooでは有効、その下の/var/www/html/foo/barでは無効化するなら以下のようになる。
<Directory /var/www/html/foo> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.+)$ /cgi-bin/notfound.cgi/$1 [L,QSA] </Directory> <Directory /var/www/html/foo/var> RewriteEngine Off </Directory>/var/www/htmlをDocumentRootとすると、http://〜/foo/に存在しないファイル・ディレクトリにアクセスした場合、/cgi-bin/notfound.cgiが処理される。かつ、/cgi-bin/notfound.cgi/foo/アクセスしたファイル・ディレクトリ名にアクセスしたのと同じことになる。 1 逆にhttp://〜/foo/bar/に存在しないファイル・ディレクトリにアクセスした場合は、rewriteされないので404エラー画面が表示される。 - RewriteCond
-
rewriteする条件を指定。例えば携帯からアクセスした場合に/mobile/に振り分ける場合
RewriteCond %{REQUEST_URI} !^/mobile/ RewriteCond %{HTTP_USER_AGENT} ^DoCoMo [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^J-PHONE [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^Vodafone [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^MOT- [NC,OR] RewriteCond %{HTTP_X_JPHONE_MSNAME} .* [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^KDDI- [NC,OR] RewriteCond %{HTTP_USER_AGENT} ^UP¥.Browser [NC] RewriteRule ^/(.*)$ /mobile/$1 [QSA,L]User-Agentで携帯かどうかを判断。NCはCase-Insensitive、ORは次のRewriteCondとOR条件でつなげるオプション。
キモは1行目の無限ループ対応処理。携帯から/productにアクセス。 ↓ /mobile/producにRewriteRule。再度ループに入る。 ↓ 1行目に合致するのでRewriteRuleされない。
指定できる環境変数はこの辺を参照。 - RewriteRule
-
書き換えのルールを指定。
RewriteRule 書き換え前URL 書き換え後URL フラグ
書き換え前URLは、RewriteRuleをどこに書くかによって変わるので注意。例としてhttp://〜/foo/test.htmlにアクセスする場合、以下のパターンでどうなるかを見る。DocumentRoot "/var/www/html" <Directory /var/www/html/foo> RewriteEngine On RewriteRule ^(.+)\.html$ /$1.php [R=301] </Directory>この場合、$1にはtestが入るので、http://〜/test.phpに飛ぶ。二つ目の例。DocumentRoot "/var/www/html" RewriteEngine On RewriteRule ^(.+)\.html$ /$1.php [R=301]上記の場合、$1には"foo/test"が入るので、http://〜/foo/test.phpに飛ぶということ。つまり、サーバ設定コンテキストや!VirtualHostにある場合は、ドキュメントルートからのパス、Directoryディレクティブの中にある場合は、そのディレクトリからの相対パスとなる。 場面ごとにフラグをどう使うかは重要!!!ここを参照。 - RewriteBase
-
書き換えのベースとなるURLを指定する。Aliasなどを使っていてURLとディレクトリ名が対応していない場合などで使用する。例えば、
DocumentRoot /var/www/html Alias /software /home/product
で、/home/product/.htaccessにRewriteEngine On RewriteRule ^cf$ cf.cfmとなっている場合に、http://〜/software/cf にアクセスすると /home/product/cf.cfmを見せたい。/software/cf が /home/product/cf に Aliasされる。 ↓ ディレクトリパス部分を取り除いて、"cf"だけにする ↓ cfがcf.cfm に Rewriteされる。 ↓ ディレクトリパス部分を元に戻して、"/home/product/cf"にする ↓ http://〜/home/product/cf.cfmにアクセス
となり404エラーとなる。これを回避する為に、RewriteEngine On RewriteRule ^cf$ cf.cfmとすると、/software/cf が /home/product/cf に Aliasされる。 ↓ ディレクトリパス部分を取り除いて、"cf"だけにする ↓ cfがcf.cfm に Rewriteされる。 ↓ ディレクトリパス部分を元に戻す際に、RewriteBaseをつけて"/software/cf"にする ↓ http://〜/software/cf.cfm にアクセス ↓ /software/cf.cfm が /home/product/cf.cfm に Aliasされる。
となる。つまりRewriteBaseは以下のような場合に.htaccessが存在しているディレクトリがURLではどこに当るのかをapacheに教える。ちとややこしい。- alias使ってる時
- .htaccessで指定している時
- RewriteLog、RewriteLogLevel
-
Rewriteの状況をログに記録する。LogLevelは検証だと3程度、本番ではOFFにすべし。
RewriteLog logs/rewrite_log RewriteLogLevel 3 <Directory /var/www/html/foo> RewriteEngine On RewriteRule ^(.+)$ /bar/$1 [L,QSA] </Directory>http://〜/foo/aaa.html にアクセスするとhttp://〜/bar/aaa.htmlにアクセスしたのと同じになる。でログ。10.211.55.6 - - [10/Sep/2007:21:56:33 +0900] [10.211.55.3/sid#82fa3f0][rid#83ea460/initial] (3) [perdir /var/www/html/foo/] strip per-dir prefix: /var/www/html/foo/aaa.html -> aaa.html 10.211.55.6 - - [10/Sep/2007:21:56:33 +0900] [10.211.55.3/sid#82fa3f0][rid#83ea460/initial] (3) [perdir /var/www/html/foo/] applying pattern '^(.+)$' to uri 'aaa.html' 10.211.55.6 - - [10/Sep/2007:21:56:33 +0900] [10.211.55.3/sid#82fa3f0][rid#83ea460/initial] (2) [perdir /var/www/html/foo/] rewrite 'aaa.html' -> '/bar/aaa.html' 10.211.55.6 - - [10/Sep/2007:21:56:33 +0900] [10.211.55.3/sid#82fa3f0][rid#83ea460/initial] (1) [perdir /var/www/html/foo/] internal redirect with /bar/aaa.html [INTERNAL REDIRECT]
実践編
メンテナンス画面への切り替え
DocumentRoot直下にmainte.htmlがあったら全てのアクセスをそちらに飛ばす。
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/mainte.html -f
RewriteCond %{SCRIPT_FILENAME} !mainte.html
RewriteRule ^.* /mainte.html [L]
でもこれだとクローラーに拾われるかも。302 Moved Temporarilyあたりがよいのかな。
RewriteRule ^.* /mainte.html [R,L]
ちなみにmod_rewriteだと30X系しか吐けないみたいなので、"503 Service Unavailable" あたりで返したい場合は、apacheだけだと無理ということで、この辺を参照。
REST対策
REST = Representational Status Transfer。
- URLとリソースが結びついている。
- URLは不変 1リソースにつき1URI。1URIに複数のリソースはだめ。とあるURLの○番目の記事とかだと、永続的に参照できる保証がないのでダメ。
- URLが実装に依存しない。
特定のプログラミング言語で組んだ場合のURLが
http://〜/○○○.php
とかでこれがperlに変わるとhttp://〜/○○○.pl
とかになっちゃいかんということ。http://〜/○○○
実装が変わったとしても上記で常にアクセスできるようにすべし。もっというと、以下のような仕様というか関数が見えるのもよくない。http://〜/login.php?action=show
仕様が変わって関数名がshowから別のものに変わると永続性がなくなる。こういう場合は隠す。RewriteEngine On RewriteRule ^/login/?(.+)?$ login.php/$1?action=show [QSA]
というかこの例ちょっとわかりにくい。それよりかは、RewriteEngine On RewriteRule ^/news/detail/([0-9]+)/? /news/detail.php?id=$1 [L]
とかみたいな、最近の流行で言ってもらった方がわかりやすい。
簡易ラウンドロビン
RewriteMapディレクティブを使うと、ちょっとおもしろいことができる。 まず以下のように/var/www/server.lstを用意しておく。
img img1.example.com|img2.example.com
次にmod_rewriteの設定
RewriteEngine On
RewriteMap imgServers rnd:/var/www/server.lst
RewriteRule ^/img/(.+)$ http://${imgServers:img}/img/$1 [R=301]
こうすると、/img/配下のファイルにアクセスした場合、http://img1.example.jp/img/* と http://img2.example.jp/img/* にランダムにアクセスされ、簡易な負荷分散ができる。
- .htaccessではできないので注意。
その他
- ドキュメント読む、これ重要!
- テストする、これ重要!
- フレームワークなんか使ってる場合にREST対策で使うためってのが主流か
- 1. ただしブラウザのURLが書き変わる訳ではなく内部的に書き変わるだけ?

