PHPでHTTPヘッダー、x-forwarded-forを受け取る方法

執筆日:

更新日:

こんにちは@mosuke5です。
もともとApach+PHPで動作していたシステムに、リバースプロキシ(Nginx)を前段に挟むことになりました。理由は、もともと社内ネットワークでのみ利用するシステムだったのですが、インターネットの外からも利用することになり、インターネットからの入り口にリバースプロキシを導入したからです。
つまり、Nginx->Apache->PHPという構成になりました。
リバースプロキシを導入した際によく問題となることだが、Apacheからみるとすべてリバースプロキシから通信がきているようにみえます。 接続元のIPアドレスがすべてリバースプロキシのもの変わってしまいます。

そこで、HTTPヘッダーに接続元のIPアドレス追加しアプリ側(PHP)で受け取る方法とその際の注意点を書きます。

リバースプロキシ側でHTTPヘッダー追加

まず、デフォルトのNginxの設定では接続元のIPアドレスをHTTPヘッダーに含みません。そのため、本当のクライアント側のIPアドレスをHTTPヘッダーに追加します。一般的にX-Forwarded-ForというHTTPヘッダーを利用します。
X-Forwarded-Forというヘッダー名にNginxでもっている変数$proxy_add_x_forwarded_forを追加する。

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 

リバースプロキシしかり、AWSのELBなどしかり、L7でのプロキシやロードバランサを利用した際に、よくX-Forwarded-Forというヘッダー名がでてきます。このヘッダーはなんなのか?HTTPの標準のヘッダーとして存在するのかと疑問に思い調べてみると、RFCには定義されていないが一般的に利用されているヘッダーであることがわかりました。

X-Forwarded-For (XFF) とは、HTTPヘッダフィールドの一つ。HTTPプロキシサーバまたは負荷分散装置(ロードバランサ)を経由してウェブサーバに接続するクライアントの送信元IPアドレスを特定する際のデファクトスタンダードである。 (略)RFCの標準的なヘッダフィールドではないが、IETFのネットワーク作業部会 (Network Working Group) は2011年10月より同種のHTTPヘッダForwardedの標準化作業を開始した[1]。

PHPでX-Forwarded-Forを受け取る

リバースプロキシで’X-forwarded-For’という名前のHTTPヘッダーを追加したので、そのまま取得しようとするが、実はこのままだとうまくいきません。PHPのサーバ変数に入れる際に、名前を規則に従って変更するからです。これは、<a href=“https://www.ietf.org/rfc/rfc3875" target="_blank>“RFC3875(CGIの仕様) のProtocol-Specific Meta-Variablesに基づいて変換されているようです。

echo $_SERVER['X-Forwarded-For'];
// -> 存在しない

サーバ変数をvar_dumpを使って中身を確認してみると、HTTPが先頭についてて、すべて大文字になってて、ハイフンがアンスコに変わっていることが確認できます。

<?php
var_dump($_SERVER);

# array(x) { ["HTTP_X_FORWARDED_FOR"] => string(12) "192.168.33.1" ...... }

ためしにgetallheadersという関数で生のヘッダーの状態を確認してみると、想定通りでした。 つまりサーバ変数に格納するタイミングで、名前が変わるということです。

<?php
var_dump(getallheaders());

# array(x) { ["X-Forwarded-For"] => string(12) "192.168.33.1" ...... }

tcpdumpで生のパケットを確認してみる

結果は見えているのですが、気になったのでtcpdumpで生パケットの状態を確認してみます。Nginxからプロキシされるときは期待通りとヘッダー名は"X-Forwarded-For “になっていること確認できました。

$ sudo yum install tcpdump
$ tcpdump dst port 80 -X

# ながいんで適当に端折りました
11:04:01.883209 IP 10.0.2.15.43038 > 192.168.0.10.54655: Flags [.], seq 802:1603, ack 1, win 14600, length 1460
     (略)
     0x0000:  4500 0355 c705 0000 3706 24ca adc2 265f  1.0..X-Forwarded
     0x0010:  c0a8 000a 0050 d57f 51ad 1e62 e596 78a4  -For:.192.168.33
     0x0020:  8018 0137 8dbe 0000 0101 080a d1dc c19e  .1..Host:.xxxxxx 

任意のHTTPヘッダーつけた場合

それでは、X-Forwarded-For以外の任意のHTTPヘッダーをつけた場合も同様になるのか確認してみます。 Nginxにて、‘my-header’という名前でヘッダーを追加してPHP側で受け取ります。

proxy_set_header my-header 'hogefugafoobar'; 

my-headerHTTP_MY_HEADERに変わっていることが確認できた。

var_dump($_SERVER);

# array(35) { ["HTTP_MY_HEADER"] => string(14) "hogefugafoobar" ...... }

さいごに

PHPでHTTPヘッダーを受け取る際の注意点について見てきました。 本ブログではNginxを用いたリバースプロキシを例にしていますが、その他にもAWS ELBなどのL7のロードバランサを用いた場合も基本的に同様ですので、原理原則をおさえておくといいでしょう。

記事の内容に関連した相談、仕事依頼したい New

記事の内容やクラウドネイティブ技術に関する相談、仕事依頼を開始しました。
仕事依頼、相談をしてみる

フィードバック

本記事に対して、フィードバックあればこちらのフォームからご記入ください。
記事の内容にフィードバックしてみる

このエントリーをはてなブックマークに追加