プロキシプロトコルとは、ロードバランサーやリバースプロキシを経由した通信において、本来の接続元IPアドレスやポート番号をバックエンドサーバーへ伝えるための仕組みです。
通常、ユーザーがWebサイトやアプリケーションにアクセスする際、途中にロードバランサーやプロキシが入ると、バックエンドサーバーから見える接続元はユーザー本人ではなく、ロードバランサーやプロキシになります。
そのため、何も対策をしない場合、サーバーのアクセスログにはユーザーのIPアドレスではなく、ロードバランサーのIPアドレスが記録されてしまいます。
プロキシプロトコルは、この問題を解決するために使われます。
ロードバランサーやプロキシが、バックエンドサーバーへ通信を転送する際に「本来のクライアントIP」「本来の接続元ポート」「接続先IP」「接続先ポート」などの情報を付加します。
これにより、バックエンドサーバーは、実際にアクセスしてきたユーザーのIPアドレスを把握できるようになります。
ロードバランサーやリバースプロキシを使う構成では、バックエンドサーバーはユーザーから直接通信を受けるわけではありません。
実際には、ユーザーからロードバランサーへ通信が届き、ロードバランサーがその通信をバックエンドサーバーへ転送します。
このとき、バックエンドサーバーから見ると、接続元はユーザーではなくロードバランサーです。
そのため、アクセスログやアプリケーション側で取得できる接続元IPが、すべてロードバランサーのIPになってしまうことがあります。
これでは、実際のユーザーごとのアクセス状況を把握できません。
IP制限、レート制限、不正アクセス調査、アクセス解析などにも支障が出ます。
プロキシプロトコルを使うと、ロードバランサーやプロキシは、バックエンドサーバーへ通信を渡す際に、本来の接続元情報を一緒に伝えます。
バックエンドサーバーがプロキシプロトコルに対応していれば、その情報を読み取り、実際のユーザーIPとして扱えます。
これにより、アクセスログに正しいクライアントIPを残したり、実IPベースでアクセス制御を行ったりできるようになります。
クライアントIPを伝える方法として、HTTPでは「X-Forwarded-For」や「X-Real-IP」などのHTTPヘッダーがよく使われます。
しかし、これらはあくまでHTTPヘッダーです。
そのため、HTTPやHTTPSのリクエストであれば利用できますが、HTTP以外のTCP通信では基本的に使えません。
一方、プロキシプロトコルは、HTTPヘッダーではなく、接続の先頭にメタ情報を付ける仕組みです。
そのため、HTTPに限定されず、TCPロードバランシングの構成でも利用しやすいのが特徴です。
ただし、すべてのTCPサービスでそのまま使えるわけではありません。
バックエンド側、またはバックエンドの前段にあるサーバーが、プロキシプロトコルを解釈できる必要があります。
プロキシプロトコルでは、ロードバランサーやプロキシがバックエンドサーバーへ接続するとき、通常のアプリケーションデータの前に、接続元情報を付加します。
つまり、バックエンドサーバーには、最初にプロキシプロトコルの情報が届き、その後にHTTPやTLSなどの通常の通信データが続きます。
この仕組みによって、バックエンドサーバーは通信の最初に「この通信の本来の接続元は誰か」を知ることができます。
プロキシプロトコルは、IPパケットそのものの送信元アドレスを書き換える仕組みではありません。
実際のネットワーク上の接続元は、あくまでロードバランサーやプロキシです。
ただし、プロキシプロトコルによって、本来のクライアントIPをメタデータとしてバックエンドサーバーに伝えます。
そのため、バックエンドサーバーのOSやネットワークレベルでは、接続元はロードバランサーとして見えます。
一方で、アプリケーションやWebサーバーがプロキシプロトコルを解釈することで、本来のクライアントIPを扱えるようになります。
ここは誤解しやすいポイントです。
プロキシプロトコルは、ネットワークの送信元IPを維持する技術ではなく、元の接続情報をアプリケーション側に伝えるための仕組みです。
PROXY protocol v1は、テキスト形式のプロトコルです。
人間が読める形式で接続情報を表現するため、比較的理解しやすく、デバッグもしやすいのが特徴です。
v1では、主に次のような情報を扱います。
| 情報 | 内容 |
|---|---|
| 通信種別 | IPv4かIPv6かなど |
| 接続元IP | 本来のクライアントIP |
| 接続先IP | 本来の接続先IP |
| 接続元ポート | 本来のクライアント側ポート |
| 接続先ポート | 本来の接続先ポート |
v1のメリットは、テキスト形式なので目視で確認しやすいことです。
トラブルシューティング時にも、通信の先頭にどのような情報が付いているかを把握しやすくなります。
一方で、バイナリ形式に比べるとやや冗長で、拡張性も限定的です。
PROXY protocol v2は、バイナリ形式のプロトコルです。
v1のように人間がそのまま読める形式ではありませんが、よりコンパクトで、機械的に処理しやすい構造になっています。
v2の大きな特徴は、拡張性の高さです。
v2では、接続元IPやポートだけでなく、追加情報を付けるための仕組みも用意されています。
たとえば、TLS関連情報、ALPN、SNI、証明書情報、クラウドプロバイダ固有のメタデータなどを扱える場合があります。
また、実務ではTCPロードバランシングで使われることが多いですが、仕様上はUDPやDGRAM系の情報も表現できます。
ただし、実際に利用できるかどうかは、ロードバランサーやサーバー側の対応状況に依存します。
X-Forwarded-Forは、HTTPリクエストの中に本来のクライアントIPを記録するためのHTTPヘッダーです。
WebアプリケーションやWebサーバーでは、クライアントIPを取得するために広く使われています。
ただし、X-Forwarded-ForはHTTPヘッダーなので、基本的にはHTTP通信の中でしか利用できません。
ロードバランサーやリバースプロキシがHTTPの中身を見られる構成であれば、X-Forwarded-Forを追加できます。
しかし、HTTP以外の通信や、TLSをロードバランサーで終端しない構成では、HTTPヘッダーを追加できないことがあります。
プロキシプロトコルは、HTTPヘッダーではありません。
HTTPリクエストの中ではなく、通信データの先頭に接続情報を付けます。
そのため、HTTPに限定されず、TCPロードバランサーやTLSパススルー構成でも利用しやすいのが特徴です。
簡単に整理すると、HTTPリクエストの中でIPを伝えるのがX-Forwarded-Forで、接続の先頭でIPを伝えるのがプロキシプロトコルです。
WebサイトやWebアプリケーションで、ロードバランサーがHTTPやHTTPSを終端している場合は、X-Forwarded-ForやX-Real-IPで十分なことが多いです。
一方で、TCPロードバランサーを使っている場合や、ロードバランサーでTLSを終端せずバックエンドサーバーでTLSを終端する場合は、プロキシプロトコルが有効な選択肢になります。
| 状況 | 向いている方法 |
|---|---|
| HTTP/HTTPSでL7プロキシを使う | X-Forwarded-For |
| HTTPヘッダーを追加できる構成 | X-Forwarded-For、X-Real-IP |
| TCPロードバランサーを使う | プロキシプロトコル |
| TLSをバックエンドで終端する | プロキシプロトコル |
| HTTP以外の通信で元IPを知りたい | プロキシプロトコル |
最もよくある利用例は、ロードバランサー配下にあるWebサーバーで、本来のクライアントIPを取得したいケースです。
ロードバランサーを経由すると、WebサーバーのログにはロードバランサーのIPが記録されがちです。
プロキシプロトコルを有効にすると、Webサーバーは本来のクライアントIPを取得できるようになります。
これにより、アクセスログの精度が上がり、不正アクセス調査やアクセス制御にも役立ちます。
プロキシプロトコルは、HTTPに限らずTCPロードバランシングでも使われます。
たとえば、独自のTCPサーバー、メール系サーバー、データベース系通信、キャッシュサーバーなどで、元のクライアントIPをバックエンド側で把握したい場合に利用されます。
ただし、対象のサーバーがプロキシプロトコルに対応していない場合、通信の先頭に付けられたプロキシプロトコル情報を通常のデータとして解釈してしまい、エラーになることがあります。
そのため、TCP系サービスで使う場合は、バックエンド側の対応状況を必ず確認する必要があります。
HTTPS通信では、ロードバランサーでTLSを終端する構成と、バックエンドサーバーでTLSを終端する構成があります。
ロードバランサーでTLSを終端する場合、ロードバランサーはHTTPの中身を確認できます。
そのため、X-Forwarded-ForなどのHTTPヘッダーを追加できます。
一方で、TLSをバックエンドサーバーで終端する構成では、ロードバランサーは暗号化された通信の中身を見ません。
この場合、HTTPヘッダーを追加することはできません。
そこで、TLSハンドシェイクの前にプロキシプロトコルの情報を付けることで、バックエンドサーバーに本来のクライアントIPを伝えられます。
この構成では、バックエンドサーバーが「最初にプロキシプロトコルを読み、その後にTLS通信を処理する」設定になっている必要があります。
HAProxyは、プロキシプロトコルを広く普及させた代表的なソフトウェアです。
HAProxyでは、バックエンドサーバーへ通信を転送する際に、プロキシプロトコルを付けて送信できます。
また、v1だけでなくv2を利用する構成もあります。
ロードバランサーやリバースプロキシとしてHAProxyを使う場合、本来のクライアントIPをバックエンドへ渡したい場面でよく使われます。
NGINXも、プロキシプロトコルを受け取る構成に対応しています。
NGINXでは、プロキシプロトコル付きの通信を受け取る設定と、受け取ったクライアントIPをログやアプリケーション側で使うための設定が必要になります。
注意点として、NGINXで本来のクライアントIPを扱うには、RealIP関連の機能を使うことがあります。
環境によっては、モジュールが有効になっているか確認が必要です。
また、NGINXのバージョンによって、PROXY protocol v2やTCPでの受信対応状況が異なるため、実際の設定時には利用しているバージョンの仕様確認が必要です。
AWSでは、Network Load Balancer、いわゆるNLBでPROXY protocol v2を利用できます。
NLBはL4ロードバランサーとして使われることが多く、TCPやTLSなどの通信をバックエンドへ転送できます。
ただし、AWS NLBでは常にPROXY protocol v2が必要というわけではありません。
構成によっては、Client IP preservationによってクライアントIPを保持できる場合があります。
一方で、ターゲットタイプやPrivateLink、ヘアピン通信、クロスVPC構成など、構成によってはPROXY protocol v2が必要または有効な選択肢になることがあります。
したがって、AWS NLBでクライアントIPを扱う場合は、Client IP preservationとPROXY protocol v2の違いを理解しておくことが重要です。
Google Cloudでも、一部のロードバランサー構成でPROXY protocolを利用できます。
ただし、「Google Cloud Load Balancer全般が同じように対応している」と考えるのは正確ではありません。
Google Cloudには複数のロードバランサー種別があり、PROXY protocolの対応状況は種類によって異なります。
たとえば、proxy Network Load BalancerやTCP proxy系の構成では、PROXY protocol v1を利用できる場合があります。
実際に利用する際は、ロードバランサーの種類ごとに対応状況を確認する必要があります。
最大のメリットは、ロードバランサーやプロキシを経由しても、本来のクライアントIPをバックエンドサーバーで取得できることです。
これにより、アクセスログの精度が上がります。
すべてのアクセスがロードバランサーのIPとして記録される状態では、ユーザー単位のアクセス解析や不正アクセス調査が難しくなります。
プロキシプロトコルを使えば、実際のユーザーIPをもとにログ分析ができます。
本来のクライアントIPを取得できれば、IPアドレスに基づく制御がしやすくなります。
たとえば、特定のIPからのアクセスを制限したり、短時間に大量アクセスしているIPを検知したりできます。
ロードバランサーのIPしか見えない状態では、すべてのユーザーが同じIPから来ているように見えてしまいます。
そのため、IPベースの制御が正しく機能しないことがあります。
プロキシプロトコルは、この問題を解決する手段になります。
X-Forwarded-ForのようなHTTPヘッダーは、基本的にHTTP通信で使うものです。
一方、プロキシプロトコルはHTTPに依存しないため、TCPロードバランシングやTLSパススルー構成でも利用しやすいです。
Webサーバー以外のバックエンドでも、対応していれば元の接続情報を扱えます。
ロードバランサーでTLSを終端しない場合、ロードバランサーはHTTPヘッダーを追加できません。
そのような構成でも、プロキシプロトコルを使えば、TLS通信の前に元の接続情報を渡せます。
バックエンドサーバーでTLSを終端したい場合や、証明書管理をバックエンド側に集約したい場合には有効です。
プロキシプロトコルは、送信側と受信側の両方が対応していなければ正常に動作しません。
ロードバランサーがプロキシプロトコルを送っているのに、バックエンドサーバーが通常のHTTPやTLSとして受け取ろうとすると、通信エラーになります。
反対に、バックエンドサーバー側だけがプロキシプロトコルを期待しているのに、ロードバランサーが通常の通信を送っている場合も、正常に処理できません。
つまり、プロキシプロトコルは「送る側」と「受ける側」をセットで設定する必要があります。
プロキシプロトコルを受け取るポートでは、基本的にプロキシプロトコル付きの通信だけを受けるべきです。
同じポートで、通常のHTTP通信とプロキシプロトコル付き通信を混在させると、通信エラーやセキュリティリスクにつながります。
運用上は、通常通信を受けるポートと、プロキシプロトコル付き通信を受けるポートを分ける構成が安全です。
プロキシプロトコルで渡されるクライアントIPは、送信元のプロキシが申告する情報です。
そのため、信頼できないクライアントが直接バックエンドサーバーに接続できる状態だと、偽のクライアントIPを申告されるリスクがあります。
たとえば、攻撃者が任意のIPアドレスを名乗るプロキシプロトコル情報を送ってきた場合、サーバーがそれを信じてしまう可能性があります。
これを防ぐには、バックエンドサーバーを外部から直接アクセスできないようにすることが重要です。
ロードバランサーからの通信だけを許可し、セキュリティグループやファイアウォールで制限する必要があります。
また、Webサーバー側でも、信頼するプロキシのIP範囲を明確に指定することが大切です。
プロキシプロトコルは、通信の先頭に特別な情報を追加します。
そのため、バックエンド側がプロキシプロトコルを理解できない場合、その情報を通常の通信データとして誤って解釈してしまいます。
Webサーバーであれば、不正なHTTPリクエストとして扱われることがあります。
TLS通信であれば、TLSハンドシェイクに失敗することがあります。
SSHやデータベース、RedisなどのTCPサービスでも、サーバー側がプロキシプロトコルに対応していなければ、そのまま利用することはできません。
必要に応じて、プロキシプロトコルを解釈できる前段サーバーや中継プロキシを用意する必要があります。
プロキシプロトコルを有効化した後に、Webサーバーで400 Bad Requestが発生する場合があります。
これは、ロードバランサーがプロキシプロトコルを送っているのに、Webサーバー側がそれを理解していないケースで起こりやすいです。
Webサーバーは、本来HTTPリクエストが来ると思っているところに、プロキシプロトコルの情報を受け取ります。
その結果、不正なリクエストとして処理されてしまいます。
この場合は、Webサーバー側でプロキシプロトコルを受け取る設定が必要です。
HTTPSやTLS通信でプロキシプロトコルを使う場合、バックエンドサーバーはTLSハンドシェイクの前にプロキシプロトコルを読む必要があります。
サーバーがいきなりTLS通信を期待している状態で、先頭にプロキシプロトコル情報が届くと、TLSとして解釈できずエラーになります。
この場合も、バックエンドサーバー側でプロキシプロトコルに対応した受信設定が必要です。
プロキシプロトコルを有効にしても、アクセスログがロードバランサーのIPのままになることがあります。
この場合、プロキシプロトコル自体は届いていても、ログに使っている変数やサーバー側のIP置換設定が正しくない可能性があります。
Webサーバーによっては、プロキシプロトコルから取得したクライアントIPを専用の変数で扱います。
また、通常の接続元IPとして扱うには、追加設定が必要な場合もあります。
バックエンドサーバーが外部から直接アクセス可能な状態でプロキシプロトコルを信頼していると、IP偽装のリスクがあります。
攻撃者が直接バックエンドに接続し、偽のクライアントIP情報を送る可能性があるためです。
このリスクを防ぐには、バックエンドサーバーへの直接アクセスを遮断し、信頼できるロードバランサーやプロキシからの通信だけを許可する必要があります。
L4ロードバランサーでは、HTTPの中身を見ずにTCPレベルで通信を転送することがあります。
この場合、X-Forwarded-ForのようなHTTPヘッダーを追加できません。
そのため、本来のクライアントIPをバックエンドへ伝えたい場合は、プロキシプロトコルが有効です。
ロードバランサーではなく、バックエンドサーバーでTLSを終端したい場合も、プロキシプロトコルが役立ちます。
ロードバランサーが暗号化された通信の中身を見ない構成では、HTTPヘッダーを追加できません。
この場合、TLS通信の前にプロキシプロトコル情報を付けることで、本来のクライアントIPを伝えられます。
HTTP以外のTCP系サービスで、元のクライアントIPを把握したい場合にも、プロキシプロトコルが候補になります。
ただし、サーバー側がプロキシプロトコルに対応している必要があります。
未対応のサーバーにそのままプロキシプロトコル付き通信を流すと、通信が失敗する可能性があります。
HTTPやHTTPSのWebアプリケーションで、ロードバランサーやリバースプロキシがHTTPヘッダーを追加できる構成であれば、X-Forwarded-ForやX-Real-IPで十分なことがあります。
特に、L7ロードバランサーやCDNを使っている場合は、HTTPヘッダーで元のクライアントIPを渡す構成が一般的です。
ロードバランサーでTLSを終端している場合、ロードバランサーはHTTPリクエストの中身を見られます。
そのため、X-Forwarded-ForなどのHTTPヘッダーを追加できます。
このような構成では、必ずしもプロキシプロトコルを使う必要はありません。
バックエンドサーバーがプロキシプロトコルに対応していない場合、無理に有効化すると通信エラーの原因になります。
この場合は、X-Forwarded-Forなど別の方法を使うか、プロキシプロトコルを解釈できる中継サーバーを用意する必要があります。
NATは、IPアドレスやポート番号をネットワークレベルで変換する仕組みです。
通信経路上で、送信元IPや宛先IP、ポート番号などが実際に書き換えられます。
一方、プロキシプロトコルは、IPヘッダーを書き換えるわけではありません。
プロキシプロトコルは、元の接続情報を通信データの先頭に付けて、バックエンドサーバーに伝える仕組みです。
実際のネットワーク接続としては、ロードバランサーからバックエンドサーバーへの通信です。
その上で、「本来の接続元はこのIPです」という情報を、メタデータとして伝えます。
そのため、NATとは目的も仕組みも異なります。
Transparent ProxyやTPROXYは、より低いレイヤーで透過的にプロキシ処理を行う仕組みです。
構成によっては、バックエンド側から見てクライアントIPを維持しているように扱える場合があります。
プロキシプロトコルは、元の接続情報を明示的にバックエンドへ渡す方式です。
バックエンドサーバー側がその情報を読み取る必要があります。
Transparent Proxyのようにネットワークレベルで透過的に処理するのではなく、プロキシプロトコルに対応したサーバーやミドルウェアが、受け取った接続情報を解釈します。
プロキシプロトコルを使うべきかどうかは、ロードバランサーやプロキシの種類によって変わります。
L7ロードバランサーでHTTPヘッダーを追加できる構成なら、X-Forwarded-Forで十分なことがあります。
一方、L4ロードバランサー、TLSパススルー、HTTP以外の通信では、プロキシプロトコルが必要になることがあります。
プロキシプロトコルを有効化する前に、バックエンド側が対応しているか確認する必要があります。
対応していないサーバーにプロキシプロトコル付き通信を送ると、通信エラーになります。
Webサーバー、アプリケーションサーバー、DB、メールサーバー、独自TCPサーバーなど、どのレイヤーでプロキシプロトコルを解釈するのかを明確にしておくことが重要です。
プロキシプロトコルを使う場合、バックエンドサーバーへの直接アクセスを防ぐことが非常に重要です。
信頼できない相手からプロキシプロトコル情報を受け取ると、クライアントIPを偽装される可能性があります。
ロードバランサーやプロキシからの通信だけを許可し、外部からバックエンドへ直接接続できない構成にする必要があります。
プロキシプロトコルを有効化しても、ログ設定が正しくなければ本来のクライアントIPが記録されないことがあります。
Webサーバーやアプリケーションが、どの変数やヘッダーを使ってクライアントIPを記録しているのかを確認することが大切です。
プロキシプロトコルとは、ロードバランサーやプロキシを経由した通信において、本来のクライアントIPやポート番号などの接続情報をバックエンドサーバーへ伝えるための仕組みです。
HTTPヘッダーであるX-Forwarded-Forとは異なり、プロキシプロトコルは通信データの先頭に接続情報を付けます。
そのため、HTTPに限定されず、TCPロードバランサーやTLSパススルー構成でも利用しやすいのが特徴です。
ただし、プロキシプロトコルは送信側と受信側の両方が対応している必要があります。
設定が合っていないと、HTTPエラーやTLSハンドシェイクエラーの原因になります。
また、プロキシプロトコルで渡されるクライアントIPは、信頼できるプロキシから送られていることが前提です。
バックエンドサーバーを外部に直接公開していると、IP偽装のリスクがあります。
実務では、次のように使い分けるとよいでしょう。
| 構成 | 主な選択肢 |
|---|---|
| HTTP/HTTPSでL7プロキシを使う | X-Forwarded-For |
| ロードバランサーでTLS終端する | X-Forwarded-For、X-Real-IP |
| L4ロードバランサーを使う | プロキシプロトコル |
| TLSをバックエンドで終端する | プロキシプロトコル |
| HTTP以外の通信で元IPを知りたい | プロキシプロトコル。ただし受信側の対応が必要 |
プロキシプロトコルは、単に「IPを渡す便利な機能」ではありません。
正しく使うには、ロードバランサー、バックエンドサーバー、ログ設定、セキュリティ設定をセットで考える必要があります。
特に重要なのは、プロキシプロトコルを送る側と受ける側の設定を必ず一致させること、そして信頼できるプロキシからの通信だけを許可することです。
以上、プロキシプロトコルとはなんなのかについてでした。
最後までお読みいただき、ありがとうございました。