【実務・中級編】 ALBにおけるX-Forwarded-Port ヘッダーと接続ポートの伝達 – クラウド&コンテナネットワーク実践ガイド

現場のSREが語る、ALBと「見えないポート番号」の正体 ― X-Forwarded-Port を使いこなす

インフラの現場で「なぜかアプリケーション側で正しくURLが生成されない」「特定のポート宛のトラフィックだけリダイレクトループする」といった怪奇現象に遭遇したことはないだろうか?

AWSのALB(Application Load Balancer)を使っていると、クライアントからのパケットは一度ALBで終端され、バックエンド(EC2やFargate)へは別のコネクションで再構築される。このとき、クライアントが「どのポートにアクセスしたか」という情報は、デフォルトのままではバックエンドに届かない。

ここで登場するのが X-Forwarded-Port ヘッダーだ。今回は、この地味だが極めて重要なヘッダーの仕様と、マルチテナント構成における実務的な罠について、ネットワークの裏側を覗きながら解説しよう。

—

1. なぜ「ポート番号」が伝わらないのか?

まずは通信フローを整理しておこう。

1. Client → https://example.com:8443/ にリクエスト
2. ALB → 8443 でパケットを受信し、ターゲットグループへ転送
3. Target → ALBからのリクエストを受信

このとき、ターゲット(App)から見ると、接続元は「ALBのIPアドレス」であり、接続先ポートは「Appがリッスンしている80番(あるいは8080番など)」になってしまう。クライアントが本来アクセスした 8443 という情報は、ALBが意図的にヘッダーを付与しない限り、アプリケーション層まで到達しないのだ。

ALBが提供する「伝言板」

ALBは、プロキシとして振る舞う際に以下の3つのヘッダーを付与してくれる。

  • X-Forwarded-For: クライアントのIPアドレス
  • X-Forwarded-Proto: クライアントが使用したプロトコル(http/https)
  • X-Forwarded-Port: クライアントが接続した宛先ポート

これらを活用することで、アプリケーションは「自分は今、どの入り口から叩かれているのか」を認識し、適切なリダイレクト先やベースURLを動的に生成できるようになる。

—

2. 実践:X-Forwarded-Port を確認する

まずは、自分の環境で実際にこのヘッダーがどう見えているかを確認しよう。Python(Flask)を使った最もシンプルな検証コードがこれだ。

from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def debug_headers():
    # ALBが付与したヘッダーを取得
    port = request.headers.get('X-Forwarded-Port')
    proto = request.headers.get('X-Forwarded-Proto')
    
    return f"クライアントが接続したポート: {port}, プロトコル: {proto}"

# 実行時は gunicorn 等のWSGIサーバーで起動してください

これをALB配下にデプロイし、curl で叩いてみると挙動がよくわかる。

# 8443ポートでリクエストを投げる(ALB側でリスナー設定が必要)
curl -I https://example.com:8443/

# 応答確認:
# X-Forwarded-Port: 8443 が返ってくるはずだ

—

3. マルチテナント構成での「危ない落とし穴」

この X-Forwarded-Port が真価を発揮するのは、単一のALBで複数のサービスや、異なるポート設定を持つテナントを収容する場合だ。

例えば、1つのドメインに対して「管理画面(8443)」と「一般API(443)」を運用しているとしよう。アプリケーション側で「もしポートが8443なら、管理用の認証フローへ回す」といったロジックを組む際、このヘッダーが信頼の拠り所となる。

ここで注意すべき「信頼性」の問題

SREとして警告したいのは、「このヘッダーはクライアントから改ざん可能である」という事実だ。

もしインターネットから直接バックエンドにアクセスできる構成なら、悪意あるユーザーが X-Forwarded-Port: 8443 を偽装してリクエストを送れば、アプリケーションはそれを信じ込んでしまう。

対策:
1. Security Groupの徹底: バックエンドのセキュリティグループは、ALBのSGからのみ通信を許可する。
2. 信頼済みプロキシ設定: アプリケーション(あるいはNginx等のリバースプロキシ)側で、X-Forwarded-* ヘッダーを信頼するIPレンジをALBのIP範囲に限定する。

—

4. 設定のポイントとトラブルシュート

現場でよくある失敗は、「ALBのリスナー設定でポートを割り当てたのに、アプリが認識しない」というケースだ。

チェックリスト

  • ALBリスナー: 該当ポート(例: 8443)でリスナーが作成されているか?
  • ターゲットグループ: ターゲットグループのプロトコルバージョンは適切か?(gRPCやHTTP/2の場合、ヘッダーの扱いが変わることもある)
  • アプリケーションのフレームワーク: Express.jsやRailsなどは、trust proxy 設定をONにしないと、これらのヘッダーを無視、あるいは上書きしてしまうことがある。

特にNode.js(Express)の場合、以下のような設定が必須だ。

// ExpressでALBのヘッダーを正しく扱うための設定
app.set('trust proxy', true); 

app.get('/', (req, res) => {
    // これを有効にしないと、req.protocol等が正しく解決されない
    console.log(req.headers['x-forwarded-port']);
    res.send('Port info processed.');
});

—

まとめ:ネットワークの透明性を確保せよ

X-Forwarded-Port は、決して目立つヘッダーではない。しかし、大規模な分散システムにおいて、リクエストの「文脈」を正しくバックエンドに届けるための生命線だ。

SREの視点から言えば、「ネットワーク機器(ALB)とアプリケーション(コード)の間で、コンテキストを共有する」という意識が、障害を未然に防ぐ鍵となる。

もし、貴方の管理するシステムで「ポート番号がズレて誤動作する」という怪奇現象に遭遇したら、まずは tcpdump やログ収集基盤で、ALBが渡しているヘッダーと、アプリが読み取っている値の「ギャップ」を追ってみてほしい。パケットは常に、嘘をつかない答えを運んでいるのだから。

コメント

タイトルとURLをコピーしました