こんにちは!クラウドの世界へようこそ。SREとして日々さまざまなシステムを裏から支えている私ですが、今回はインフラやネットワークの世界に一歩踏み出したばかりのあなたに向けて、とっても大切で、知っておくとちょっとドヤ顔できる「ロードバランサーのヒミツ」をお話ししちゃいます。
クラウドを使っていると必ずお世話になるのが、AWSのALB(Application Load Balancer)などのロードバランサーですよね。「トラフィックを上手に分散してくれる便利な門番」くらいのイメージを持っている方が多いのではないでしょうか?
今回は、その門番がこっそりバックエンドのサーバー(アプリ)に伝えている、X-Forwarded-Port というちょっとお名前の長いヘッダーに焦点を当ててみたいと思います。「難しそう…」なんて身構えなくて大丈夫です。身近な例えを交えながら、一歩ずつ優しく紐解いていきましょう!
—
1. 郵便配達でイメージする「宛先ポート番号」の役割
まずは、インターネットの世界を離れて、私たちの身近にある「郵便配達」に例えて考えてみましょう。
大きなマンション(バックエンドのサーバー)に、たくさんの住人(複数のWebアプリケーションやサービス)が暮らしているとします。そこにたくさんの荷物(HTTPリクエスト)が届きます。
マンションの入り口には、管理人さん(ALB)がいて、届いた荷物を受け取り、どの部屋の住人のものかを仕分けて各部屋へ届けます。
ここで問題です。管理人さんは、荷物の宛先をどうやって確認しているでしょうか?
「〇〇号室の山田様宛」という宛先が書かれているから分かりますよね。
ネットワークの世界でもこれと全く同じことが起きています。
インターネットからやってくる通信には、「どの扉(ポート番号)をノックして欲しいか」という情報が含まれています。例えば、通常のWebサイトなら 443 番(HTTPS)、ちょっと古いテスト環境などでは 80 番(HTTP)といった具合です。
ALBが間に立つのに、なぜポート番号が消えちゃうの?
ここで少し厄介な問題が起きます。
インターネットからの通信を、まずは私たちのヒーローであるALB(ロードバランサー)が受け止めます。この時、クライアントは ALBに対して通信を投げています。
ALBは、その通信を安全に受け取ったあと、今度は自分のお友達であるバックエンドのサーバー(EC2やコンテナなど)へ、「こういうリクエストが来たよ!」と新しく通信を貼り直して伝えます。
この「貼り直し」のときに、「クライアントがもともとどのポート(扉)にアクセスしたかったのか」という情報が、そのままでは消えてしまうことがあるのです。
「あれ? このリクエスト、元々は安全な 443 番宛てに来たんだっけ? それとも別のカスタムポートだったっけ?」
バックエンドのアプリからすると、これが分からないと困ってしまうケースがあるんですね。そこで登場するのが、今回主役の X-Forwarded-Port ヘッダーです!
—
2. X-Forwarded-Port ヘッダーってなに?
X-Forwarded-Port とは、一言で言うと 「管理人さん(ALB)が、バックエンドのアプリにこっそり教える『お客様が最初にノックした扉の番号』メモ」 です。
ALBは、クライアントからのリクエストをバックエンドへ転送する際、HTTPリクエストのヘッダー(おの手紙の余白みたいな場所)に、次のような情報をこっそり書き足してくれます。
X-Forwarded-Port: 443
これを見たバックエンドのアプリは、「なるほど、このユーザーは安全な通信(443 番)経由でアクセスしてきているんだな」と正確に知ることができるわけですね。
—
3. どんなときに役に立つの?マルチテナント構成の現実
「ポート番号くらい、決まったものを使えばいいじゃない」と思われるかもしれません。しかし、現場のシステムはそんなに単純ではありません。ここで、実務でよくある「マルチテナント構成」を例に見てみましょう。
例えば、1つのアプリケーションサーバー(マンションの1部屋)で、複数の企業やブランド(テナント)向けのWebサイトを同時に動かしているとします。
- A社のサイト用には、特別な設定として
8443番ポートでアクセスを受け付けたい - B社のサイト用には、通常の
443番ポートで受け付けたい
もしバックエンドのアプリが、「自分に届いた通信のポート」しか見られないとしたらどうでしょう? ALBが後ろのサーバーへはいつも決まったポートで話しかけていたら、アプリ側は「今、誰宛ての通信を処理しているんだっけ?」と迷子になってしまいます。
ここで X-Forwarded-Port が大活躍します!
クライアントがアクセスした元のポート番号(443 なのか 8443 なのか)をこのヘッダーに入れてアプリに教えてあげることで、アプリ側は「おっ、今回は 8443 宛てだからA社のデザインを表示しよう!」と賢く切り替えることができるのです。
—
4. アプリケーション側での受け取り方を見てみよう
それでは、実際にバックエンドのプログラムが、このヘッダーをどのように受け取って処理しているのか、簡単なPython(Flaskという軽量フレームワーク)のコード例で覗いてみましょう。
難しく考えず、「お便りの封筒からメモを取り出す作業」だと思って眺めてみてくださいね。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def handle_request():
# ALBがこっそり添えてくれた「X-Forwarded-Port」ヘッダーの値を読み取ります
# もしヘッダーが無い場合は、デフォルトで安全な 443 を仮置きします
client_port = request.headers.get('X-Forwarded-Port', '443')
# クライアントがアクセスしてきたプロトコル(httpsなど)も一緒に取得すると便利です
client_proto = request.headers.get('X-Forwarded-Proto', 'https')
# ログに出力してみる(実際の現場ではここで条件分岐を行います)
print(f"お疲れ様です!クライアントは {client_proto}:// 経由で、ポート番号 [{client_port}] にアクセスしてきましたよ。")
return f"あなたのアクセスしたポート番号は {client_port} です!", 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
このように、アプリ側はたった数行のコードを書くだけで、ユーザーがどの扉を叩いてやってきたのかを正確に把握できるようになります。
—
5. インフラ(AWS)側での設定のポイント
「じゃあ、AWSのALBでは特別な設定をしなきゃいけないの?」という疑問が湧きますよね。
実は、AWSのALB(Application Load Balancer)は、HTTP/HTTPSリクエストをバックエンド(ターゲットグループ)に転送する際、デフォルトでこれらの転送元情報を表すヘッダーを自動的に付与してくれます。
具体的には以下の3つがセットで伝達されます。
1. X-Forwarded-For: クライアントの実際のIPアドレス
2. X-Forwarded-Proto: クライアントが接続したプロトコル(http / https)
3. X-Forwarded-Port: クライアントが接続したポート番号
そのため、インフラ構築の初期段階では「ALBがデフォルトでよしなにやってくれる!」と覚えておくだけで十分に頼もしい状態です。
ただし、注意点として、もしバックエンドの手前にさらに別のリバースプロキシやCDN(CloudFrontなど)が挟まっている場合は、このヘッダーが多重に上書きされたり追加されたりすることがあります。
そんなときは、「一番最初に受け止めた正しいポート番号はどれだっけ?」と、プロキシチェーンの順番を意識してトラフィックのフローを紙に書き出してみるのが、トラブルシューティングの近道です。
—
まとめ
いかがでしたでしょうか?今回は少し地味だけどとっても大切な X-Forwarded-Port ヘッダーについて、郵便配達の例えを交えながら解説しました。
- ALBはクライアントとバックエンドの間で通信を受け継ぐ「優秀な管理人さん」
- クライアントがどのポート(扉)にアクセスしたのかという情報は、そのままでは後ろのサーバーに消えてしまいがち
- それを解決するために
X-Forwarded-Portヘッダーという「宛先メモ」をこっそり添えてくれている - マルチテナント構成などで、アプリが宛先ごとに挙動を変えたいときにめちゃくちゃ役に立つ
インフラやネットワークの世界は、目に見えないパケットが飛び交うため最初は難しく感じられますが、「誰が・どこから・どういう意図で情報を運んでいるのか」を紐解いていくと、パズルのようにスッと理解できるようになります。
今回の記事が、あなたのクラウドライフやコンテナネットワークの冒険の小さな道しるべになればとっても嬉しいです。それでは、また次回の技術解説でお会いしましょう!
コメント