結論
接続元のIPに応じて何かする RewriteCond で ReverseProxy ある場合も考慮した場合のよくやる設定メモ。
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,1\.1\.1\.1,
RewriteRule .* - [E=special_ip:1]
解説
-
%{REMOTE_ADDR}
に直接の接続元のIPが入ってくる。 -
%{HTTP:X-Forwarder-For}
には ReverseProxy が存在した場合の接続元IPが入ってくる。なければ空文字になる。 - 2つの変数に対して同じIPマッチを
RewriteCond %{変数} ^IP$ [OR]
みたいに[OR]
で2回書く例を結構見るが、明らかに冗長で対象IPが増えてくると見通しが悪くなる上にIP許可は大抵複数列挙したい事が多いので1行に収めたい。 - あとそもそも
X-Forwarded-For
のことを書いていても複数のIPが入ってくることが考慮されてない設定例をよく目に見るのも気になってた。 - ここで2変数を1つの文字列
,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For},
に纏めることにする。- RewriteCond の第1引数は実はただの文字列なので複数の変数を繋げられるし固定の文字も書ける
-
%{REMOTE_ADDR}
は常に値が入る -
%{HTTP:X-Forwarded-For}
は以下の3種類のパターンがあり、必要な値は一番左に書かれた値である-
空文字
これはReverseProxyが無い場合 -
1.1.1.1
など1つのIP。これはReverseProxyが1段あるケース -
1.1.1.1, 10.0.0.11
など2つ以上のIPが,
で連結された値。これはReverseProxyが多段の場合で、後段のReverseProxyに対する接続元は右に増えていくことになっている。
-
-
,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For},
を考えると以下のようなパターンになる-
,1.1.1.1,,
ReverseProxyが無い(1.1.1.1->サーバ) -
,10.0.0.11,1.1.1.1,
ReverseProxyが1段ある(1.1.1.1->10.0.0.11->サーバ) -
,10.0.0.22,1.1.1.1, 10.0.0.11,
ReverseProxyが2段ある(1.1.1.1->10.0.0.11->10.0.0.22->サーバ) -
,10.0.0.33,1.1.1.1, 10.0.0.11, 10.0.0.22,
ReverseProxyが3段ある(1.1.1.1->10.0.0.11->10.0.0.22->10.0.0.33->サーバ)
-
- 2変数をカンマで連結して更に頭と尻尾にもカンマを付けておくことでReverseProxyの有無や数に関わらず、必要なIPは
,1.1.1.1,
でマッチ出来る事が値の例から分かる。正規表現の^
と$
の代わりにカンマを置いとけば部分ではなくちゃんと完全一致になるし、他のReverseProxyのIPと紛れる事もない。 - 複数のIPチェックを列挙するケースが多いと思うがその場合は↓こうなって2行ずつ書くよりは見通しが良くなると思う。
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,1\.1\.1\.1, [OR]
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,8\.8\.8\.8, [OR]
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,8\.8\.4\.4,
RewriteRule .* - [E=special_ip:1]
-
RewriteRule
の第1引数を.*
にしてるのは「何でもマッチする」の意味です。- 昔は
.
って書いてた時期もあったんだけど、httpd.conf に書くならそれでいいけど、.htaccess
でもコピペで同じ設定が使われることを考慮すると.htaccess
だとそのディレクトリ自体へのアクセスの場合に RewriteRule のマッチ対象のパス文字列は空文字
になるので、すなわち.
だとマッチしなくて意図に反するので.*
でやってます。
- 昔は
-
RewriteRule
の第2引数は環境変数セットするだけとかの場合はなんにも使われないけど何か書く必要があるので何となく未定義的な意図で-
を書いてます。なので値が-
である事に意味はありません。 -
RewriteRule
の第3引数で環境変数をセットする場合は[E=foo]
とするだけでもデフォルト値としてfoo
には1
がセットされるけど、そのデフォルト値の仕様を知らない人がこの設定を見た場合でも「マッチしたらfooに1が入るんだな」と直感的に分かりやすいよう[E=foo:1]
と明記してます。
注意点
- ユーザのIPを
X-Forwarded-For
ではなくX-Real-IP
に入れる文化のことも考慮した場合はRewriteCondの第1引数を,%{REMOTE_ADDR},%{HTTP:X-Real-IP},%{HTTP:X-Forwarded-For},
とかにしといてやれば同じ理屈で上手く動く筈です。 -
X-Forwarded-For
やX-Real-IP
などのヘッダはユーザが勝手に詐称することも可能なので、ReverseProxy は信頼できない接続元からこれらヘッダが送られてきた場合は捨てるようになっている事は前提条件です。その部分の詐称を考慮するのはバックエンドサーバの責務じゃないので。