ウェブサーバで、クライアントの接続元IPアドレスを見て挙動を変えたいということはよくある。社内からのアクセスだけ特別扱いしたり、携帯電話キャリアのからのアクセスの時だけコンテンツを表示するなど、用途は広い。

普通、このIPアドレスは REMOTE_ADDR という変数名で取得できる。これはもともと、CGI で使われた仕様(http://tools.ietf.org/html/draft-robinson-www-interface-00)であるようだ。Apache の mod_rewrite なら %{REMOTE_ADDR} で、PHP なら $_SERVER[‘REMOTE_ADDR’] で参照できる。

ところがロードバランサやプロキシを間に挟むと、REMOTE_ADDR がロードバランサ等のアドレスになる。この場合、多くのロードバランサ等では HTTP のリクエストヘッダに X-Forwarded-For フィールドを追加する。これは HTTP の仕様ではないものの、デファクトスタンダードだ。

REMOTE_ADDR は IPレイヤの情報だが、X-Forwarded-For は HTTPレイヤの情報である。データの取得元が変わるので、値の取り方も変わる。mod_rewrite なら %{HTTP:X-Forwarded-For} だし、PHP なら $headers = apache_request_headers(); $ip = $headers[‘X-Forwarded-For’]; となる。

IPレイヤの REMOTE_ADDR とは違い、HTTPレイヤの X-Forwarded-For はクライアントによる偽装が容易なので、インターネットなどの信頼できないネットワークから直接HTTPリクエストを受ける経路も存在する場合、X-Forwarded-For のみを信じて大事な情報の表示可否を決めてはいけない。REMOTE_ADDR を併用したチェックが欠かせない。

HTTP で便利に使える X-Forwarded-For だが、HTTPS では利用できない。HTTPS では通信がエンドトゥーエンドで暗号化されるため、途中に挟まるプロキシ等が X-Forwarded-For ヘッダを挿入することができない。REMOTE_ADDR もプロキシのものになってしまって元のIPアドレスがわからないし、X-Forwarded-For も使えないので、アクセス元を判定できない。

HTTPS でアクセス元IPアドレスを知りたい場合は、ロードバランサやプロキシを通さずに HTTPS を受けるようにするか、SSLアクセラレーションあるいは SSLターミネーションと呼ばれる機能を使ってロードバランサで SSL を受け、復号化してウェブサーバに送るようにすればよい。