apacheのmod_rewriteの設定をどこよりもわかりやすく解説
mod_rewrite モジュールは、WEBサーバー(apache)上で「URL の書き換え」を行うためのモジュールです。
WordPressなどでも標準的にも使われているくらい使用頻度が多く、便利で必要不可欠なモジュールです。
ところが一方で設定が超難解であるとともに、ひとつのミスで大混乱&大障害を招いてしまうというやっかいなエンジニア泣かせのモジュールでもあります。
そうならないために基本を理解しておくことが重要です。
mod_rewriteを学ぶ際に大事なこと
難解で有名なmod_writeですが基本的な動きを知らずに使っている人がほとんどです。
一番大事なのは大まかな処理の流れの基本をおさえることです。
おさえていないとまず確実にハマります。
それはフラグやオプションの使い方ではありません。
調べてもまとめてのっているサイトはそんなになかったです。
ので、今回は体型的にまとめてみたのでよろしければご覧ください。
mod_rewriteの基本【必読!】
基本的には設定の構成は以下のようになっています。
mod_rewriteはシンプルに言うと「ルールを順番に書き並べていく」のです。
あとはおまけだと思ってください。
多くの場合は.htaccessファイルの中か、VirtualHostディレクティブ内に書くことになると思います。
RewriteEngine On
RewriteBase /
#ルール1
RewriteCond ②変数&条件
RewriteCond ③変数&条件
RewriteRule ①転送元正規表現 ④転送先URL
#ルール2
RewriteCond ⑥変数&条件
RewriteCond ⑦変数&条件
RewriteRule ⑤転送元正規表現 ⑧転送先URL
設定にたいして
mod_rewriteは①→⑧のような順に処理が進んでいきます。
最後まで終わり、パスが書き換えられるとまた①に戻り置換後のパスを再評価します。
今超重要なことを言いました!!!
ここを理解していないと確実にハマります。
もう一度いいます。
mod_rewriteは①→⑧のような順に処理が進んでいきます。
最後まで終わり、パスが書き換えられるとまた①に戻り置換後のパスを再評価します。
順番がややこしいですし、また①から始まるとは。。。。
(※勘違いしやすいのですが、たとえフラグ[L]を使っても①に戻ります)
しかもリダイレクトの無限ループが起こりやすいです。
強めにいうと「なんでこんなわかりづらい記述にすんだよ!」と言ってやりたいです。
設定が増えてくるとまるでマルチスレッドのプログラミングをしているようです。
だらだらと文章で書いて申し訳ないですが、ここを絶対に理解してください。
RewriteCond ②変数&条件
RewriteCond ③変数&条件
RewriteRule ①転送元正規表現 ④転送先URL
①のルールの転送元の設定がマッチしたら、まずその設定箇所の評価を開始。
②、③の順に条件を評価し、条件にまたマッチしたら④のURL置換処理を行います。
そして次のルールへ。
ルール設定の最下部までいって、途中の置換があればまた最初から再評価が始まります。
ルールの適用がなくなればそこで処理は終了します。
ここがクセモノで最初は理解できないでハマることでしょう。
無限ループが起こるのもこれを理解していないからの場合が多いです。
ひとつひとつ説明しますので安心してください。
mod_rewriteの各設定項目
RewriteEngineの設定
RewriteEngine Onでmod_rewriteの機能を有効化。Offで無効化。
例)
RewriteEngine On
RewriteBaseの設定
転送先URLで相対パスを使った場合のためのベースパス。
RewriteBase / とするとどのディレクトリに設置を行った場合でも必ずドキュメントルートからのパスになります。
ないと.htaccessを設置した位置からのパスになる。
※省略→現在のディレクトリ
※/を指定→ドキュメントルート
例)
RewriteBase /
RewriteRuleの設定
RewriteRuleではURL転送の設定をします。
記述方法の基本は以下です。
RewriteRule 転送元正規表現 転送先URL
転送元
正規表現を使います。ここにマッチしないリクエストはこのRewriteRule自体無視します。(上に記述しているRewriteCondの評価には進まない)
転送先
・URLのパス(https://****/、または/***からでもどっちでも)
・ファイルのパス(ファイルがあればこっちと判定)
・ハイフン(置換しない)
例)
#トップにアクセスしたらtop.phpで処理する
RewriteRule ^$ top.php
#ファイル名を置換
RewriteRule index1.html index2.html
RewriteRuleはRewriteCondと組み合わせて使うことができます。
適用のルールがこれまたややこしいです。
RewriteRuleには上にあるRewriteCondが採用されます。
書く順番と処理が流れる順番が・・・・逆・・・
例)RewriteRuleに対し適用されるRewriteCond
RewriteCond →【A】の条件
RewriteCond →【A】の条件
RewriteRule【Aのルール】
RewriteRule【Bのルール】 →条件なし
RewriteCond →【C】の条件
RewriteRule【Cのルール】
RewriteRuleのオプションで使えるフラグ
RewriteRuleには様々なオプションが用意されています。
一気に覚えようとしてもおそらく無理なので都度実例を交えながら参照してみてください。
ものすごくよくある[L]の使い方の勘違いですが、今検証のループを1回だけ途中で抜けるだけで、また置換後のURLで再度検証されるということは忘れないでください。
プログラミングいうところのループのcontinue(スキップ)であってbreak(終了)ではありません。
フラグ | 概要 |
R | リダイレクト(302) R=●とすると変更できる。 例)RewriteRule images/(.*) http://test.com/images/$1 [R,L] |
F | アクセス禁止(403) |
L | マッチしたらURL置換処理中止。 続くRewriteRuleを評価しない |
N | マッチしたらURL置換処理を最初にからやりなおす |
NC | 大文字小文字の区別をせずにマッチ |
QSA | クエリー文字列追加 |
NE | URLエスケープ処理しない |
END | apache2.4から |
PT | |
S=数字 | 数字番目のルールをスキップ |
E=●:○ | ●という名前の環境変数の値を○に変える |
P | プロキシとして処理する |
G | URL消去(410) |
C | 次のRewriteRuleにもRewriteCondを適用 |
T=●● | MIMEタイプを●●にする |
NS | メインのURLリクエストの場合のみ条件を評価。 Webサーバー機能からの内部のサブリクエストの場合などはスキップ。 |
RewriteCondの設定
RewriteCondはURLパターンの評価に続いて、マッチするさまざまな条件を評価します。
URLやドメイン名、ユーザーエージェントなど追加の評価を複数記述できます。
RewriteCond 対象文字列 演算子&評価パターン [オプションフラグ]
例)リクエストされたファイルが存在したら
RewriteCond %{REQUEST_FILENAME} !-f
例)ホスト名かつユーザーエージェントを指定
RewriteCond %{HTTP_HOST} ^www.example.com$
RewriteCond %{HTTP_USER_AGENT} MSIE
例)ホスト名もしくはユーザーエージェントを指定
RewriteCond %{HTTP_HOST} ^www.example.com$ [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE
RewriteCondの演算子
! | 文字列の前につけることで、マッチしないパターンを指定 |
< | 文字列比較 |
> | 文字列比較 |
= | 文字列比較 |
-d | 指定したディレクトリが存在すればtrue |
-f | 指定したファイルが存在すればtrue |
-s | ファイルが存在し、有限のサイズを持っていればtrue |
-F | アクセス可能な有効なパスを指している場合にtrue(内部サブリクエストを用いてcheckされる) |
-U | アクセス可能な有効なURLとなっている場合にtrue(内部サブリクエストを用いてcheckされる) |
環境変数
WEBサーバーの環境変数が使用でき、phpなら$_SERVER変数で確認できるものです。
環境変数等を用いるには %{変数名}と記述。
グループ | 変数名 | 値・意味 | サンプル |
HTTP ヘッダ | HTTP_USER_AGENT | ユーザーエージェント | Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0 |
HTTP_REFERER | 参照元URL | ||
HTTP_COOKIE | クッキー情報 | ||
HTTP_FORWARDED | プロキシ情報 | ||
HTTP_HOST | サーバーのホスト名 | ドメイン名 | |
HTTP_PROXY_CONNECTION | プロキシを経由しているか否か | ||
HTTP_ACCEPT | ブラウザのメディアMIMEタイプ | ||
コネクション & リクエスト | REMOTE_ADDR | ユーザの IP アドレス情報 | |
REMOTE_HOST | ユーザーのホスト名) | ||
REMOTE_USER | リモートユーザー名 (基本認証利用時) | ||
REMOTE_IDENT | リモートユーザーのID | ||
REQUEST_METHOD | リクエストメソッド | ||
SCRIPT_FILENAME | スクリプトファイル絶対パス | /var/www/html/index.php | |
PATH_INFO | パス情報 | ||
QUERY_STRING | クエリ文字列 | a=b | |
AUTH_TYPE | 認証タイプ | ||
サーバ内部変数 | DOCUMENT_ROOT | ドキュメントルートのパス | /var/www/html |
SERVER_ADMIN | サーバー管理者情報 | ||
SERVER_NAME | サーバー名 | ||
SERVER_ADDR | サーバーのアドレス | ||
SERVER_PORT | サーバーのポート番号 | ||
SERVER_PROTOCOL | プロトコルバージョン | ||
SERVER_SOFTWARE | サーバーソフトウェア | ||
システム関連 | TIME_YEAR | 年 | |
TIME_MON | 月 | ||
TIME_DAY | 日 | ||
TIME_HOUR | 時 | ||
TIME_MIN | 分 | ||
TIME_SEC | 秒 | ||
TIME_WDAY | 曜日 (0:日 ~ 6:土) | ||
TIME | 年月日時分秒 (例:20130123123456) | ||
特別なもの | API_VERSION | APIバージョン | |
THE_REQUEST | リクエスト文字列 | GET /index.html HTTP/1.1 | |
REQUEST_URI | リクエストURI | /?a=b | |
REQUEST_FILENAME | リクエストされたファイル名 | ||
IS_SUBREQ | サブリクエストか否か | ||
HTTPS | HTTPSでのアクセスか否か。on/off | ||
ENV:環境変数名 | %{ENV:…} 式を指定して環境変数に基いて条件分岐を行なうことができます。 |
RewriteCondのオプションで使えるフラグ
例)ドメイン名を変更して301転送
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule ^(.*)$ http://www.example.co.jp/$1 [R=301,L]
NC | 大文字小文字の区別をせずにパターンマッチを行う。「no case」 |
OR | RewriteCondの条件を「or」でつなぐ |
動作確認
かんたんな動作で細かい動きを見ていきましょう。
頭を使って疲れるかもなのでいったん読み飛ばしていただいても結構です。
ルール設定がない状態で動きを検証
・/var/www/html/index.htmlを置く
・DocumentRoot /var/www/html
・/var/www/html/.htaccessを配置
DirectoryIndex index.php index.html
<IfModule mod_rewrite.c>
RewriteEngine on
</IfModule>
確認のためhttps://***にアクセス
①メインリクエストの/を調査→設定がないので当然mod_writeは無視
②DirectoryIndexがindex.phpに内部アクセス(サブリクエスト)→mod_writeは無視
③DirectoryIndexがindex.htmlに内部アクセス(サブリクエスト)→mod_writeは無視
結果としては③でindex.htmlが見つかるので表示
かんたんなルールで動作確認
・/var/www/html/index1.htmlを置く
・DocumentRoot /var/www/html
・/var/www/html/.htaccessを配置
DirectoryIndex index.php index.html
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ index1.php #←★追加
</IfModule>
確認のためhttps://***にアクセス
※注意点としてはDirectoryIndexで発生した内部サブリクエストの転送元URLは空で入ってくること。index.phpやindex.htmlで検出はできません。
①メインリクエストの/を調査→追加された★設定に反応。内部リクエストindex1.phpが発生
②再度、今度は/index1.phpの新しいmod_rewrite処理が最初から始まる。→設定がないので無視
結果として見つかった/index1.phpが表示される
連続したルールの動作確認
・/var/www/html/index1.htmlを置く
・/var/www/html/index2.htmlを置く
・/var/www/html/index3.htmlを置く
・DocumentRoot /var/www/html
・/var/www/html/.htaccessを配置
DirectoryIndex index.php index.html
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ index1.html ★A
RewriteRule index1.html index2.html ★B
RewriteRule index2.html index3.html ★C
</IfModule>
①メインリクエストの/を調査→追加された★A設定に反応。内部リクエストindex1.htmlが発生
②続けて内部リクエストのindex1.htmlを調査→追加された★B設定に反応。内部リクエストindex2.htmlが発生
③続けてま内部リクエストのindex2.htmlを調査→追加された★C設定に反応。内部リクエストindex3.htmlが発生
結果として最後のindex3.htmlが表示されます
とにかくサンプル
WordPressより
#ファイルやディレクトリがなければ常にindex.phpを参照
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
一時的にメンテナンス画面にしたい
#管理者IPアドレスの場合は除きすべてのアクセスを作成した/maintenance.htmlの表示に切り替える
ErrorDocument 503 /maintenance.html
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !=/maintenance.html
RewriteCond %{REMOTE_ADDR} !=192.168.0.4
RewriteCond %{REMOTE_ADDR} !=192.168.0.5
RewriteRule ^.*$ - [R=503,L]
</IfModule>
<IfModule mod_headers.c>
Header set Retry-After "Sun, 14 Jun 2009 6:00:00 GMT"
</IfModule>
正規表現のなどでマッチしたURL自体を使って置換
URLを/aaa/index.html → /aaa/ などのように変えたいときなどは正規表現のグループ化特殊変数の$1,$2,…などが使えます
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_URI} ^.*/index\.html$
RewriteRule ^(.*)index.html$ https://www.example.com/$1 [R=301,L]
httpアクセスをhttps(SSL/TLS)に自動的にリダイレクトさせる
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
mod_writeの動作確認しながらデバッグをしたい
RewriteLog設定を使うことでデバッグのためにRewriteのログを出すことができます。
それぞれ数字が大きいほどログの量が多くなります。
※.htaccessでは書けません。httpd.conf、、またその外側に書きましょう
Apache2.2の場合
レベルとログ出力場所を指定できます。
RewriteLog /tmp/rewrite.log
RewriteLogLevel 3 #0~9
Apache2.4の場合
2.2の設定は廃止されました。
#( trace1~trace8)
LogLevel rewrite:trace8
ログ出力はapacheのログにされます
おまけ
リクエストとサブリクエスト(内部リクエスト)
簡単に言うとリクエストはユーザーがたたいてきたURL。
サブリクエストや内部リクエストはWebサーバーなどがサーバー内部で作り出したリクエストのことです。
コメント