リバースプロキシ

リバースプロキシとは

リバースプロキシは、クライアントからのリクエストを直接アプリサーバーに渡さず、間に立って中継するサーバーです。代表的な実装が Nginx で、静的ファイル配信からSSL終端、複数サービスへの振り分けまで幅広く使われます。

ブラウザ --HTTPS--> Nginx(TLS終端 + リバースプロキシ) --HTTP--> アプリサーバー(内部ポート)

初学者向けポイント

  • 「プロキシ」は本来クライアント側の代理(フォワードプロキシ)。リバースプロキシはサーバー側の代理という逆方向の役割
  • クライアントからは常にリバースプロキシだけが見え、背後のアプリサーバーの台数や構成を隠せる(セキュリティ・柔軟性の両面で有利)
  • 証明書の管理([[HTTPS・TLS]] の終端)を1箇所に集約できるため、アプリサーバー側は平文の [[HTTP]] だけを話せばよくなる

Nginxが担う主な役割

役割内容
SSL終端HTTPSの復号をここで行い、内部はHTTPで通信
静的ファイル配信画像・JS・CSSを直接返し、アプリサーバーの負荷を減らす
リバースプロキシlocation /api/ のようにパスごとに転送先を振り分ける
圧縮・キャッシュgzip圧縮やレスポンスキャッシュで応答を高速化
server {
  listen 443 ssl;
  location / {
    proxy_pass http://app:3000;
    proxy_set_header Host $host;
  }
}

1つの入口で複数のサービスを受ける

外に公開するアドレスとポート(443番)は1つのまま、背後の行き先をいくらでも増やせるのが強みです。振り分けの手がかりは2つあります。

  • ホスト名Host ヘッダーを見て app.example.comblog.example.com を別の転送先へ渡す。DNS上は両方が同じIPアドレスを指していてかまいません
  • パス — 同じドメインのまま /api/ はアプリサーバー、それ以外は静的ファイル、と分ける

アプリ側は3000番・8000番といった内部ポートで待ち受け、外部には公開しません。入口が1つのままなので、背後の構成を組み替えても公開URLは変わりません。

転送すると元の情報が消える

アプリサーバーに届くリクエストはリバースプロキシが作り直したものです。放っておくと、本来のクライアントの情報が失われます。

失われるもの起きること補う方法
クライアントのIPアドレスアクセスログが全件プロキシのIPになるX-Forwarded-For で元のIPを伝える
元のホスト名アプリが組み立てるURLが内部の名前になるHost / X-Forwarded-Host を渡す
HTTPSで来たという事実アプリが「平文で来た」と誤解するX-Forwarded-Proto を渡す

前掲の設定例の proxy_set_header Host $host; が、この「元のホスト名を伝える」1行です。書き忘れると、ログイン後のリダイレクト先が http://app:3000/... のような内部アドレスになり外から開けない症状が出ます。3つ目を渡さない場合のリダイレクトループは [[HTTPS・TLS]] に詳しくあります。

なお X-Forwarded-For はクライアントが自分で付けられるヘッダーでもあります。外から届いた値をそのまま信用してはいけません — 信頼するプロキシが上書きした値だけを使う設定にしないと、IP制限を偽装で抜けられます。

つまずきやすい失敗

  • 502 と 504 は別物 — 502 Bad Gateway は「後段に繋がらない/壊れた応答が返った」、504 Gateway Timeout は「繋がったが時間内に応答が返らなかった」。どちらもプロキシ自身は生きているので、まず後段を疑います([[Webサーバーとアプリケーションサーバー]])
  • WebSocketだけ繋がらない — [[WebSocket]] は通常のHTTPリクエストから接続を格上げして始まるため、UpgradeConnection ヘッダーを転送する設定を足さないと切れます。「手元では動くのに本番だけリアルタイム更新が来ない」の定番原因です
  • 大きなアップロードだけ弾かれる — プロキシ側のリクエストサイズ上限は既定値が小さく(Nginxでは1MB)、超えると413が返ります。アプリを直接叩けば通るのにプロキシ経由だと失敗するならここを疑います
  • タイムアウトが二重にある — 待ち時間の設定はプロキシとアプリの双方にあり、短いほうが先に効きます。重い処理はプロキシ側も伸ばさないと、アプリが動作中でも504が返ります

ロードバランサとの関係

1台のリバースプロキシが複数台のアプリサーバーへ振り分けると、それは事実上 [[ロードバランサ]] としても機能します。小規模構成ではNginx1台が両方の役割を兼ねることも珍しくありません。

次に読む記事の地図

入口に立つ技術なので話題が多方向へ伸びます。目的別に整理します。

「プロキシ」の向きを整理する — 同じ名前でも立ち位置で役割が反転します。[[プロキシサーバー]] はクライアント側の代理(フォワードプロキシ)で、アクセス制御やログ記録が主目的。両者を並べた比較表がそちらにあります。

実際に動かすソフトウェアを知る — [[Nginx]] が代表格で、設定ファイルの構造や nginx -t での構文チェックといった運用手順はそちらにあります。[[Docker]] 構成では入口のNginxコンテナだけがポートを公開する形が定番です。

入口に機能を足していく — 「中継するだけ」から段階的に育ちます。転送先を複数にすれば [[ロードバランサ]]、応答を保存して返せば共有キャッシュ([[HTTPキャッシュ]])、認証やレート制限まで引き受けさせれば [[APIゲートウェイ]] です。どこまでを入口に寄せるかが設計判断になります。

後ろで何が動いているかを知る — 転送先の役割分担は [[Webサーバーとアプリケーションサーバー]] が扱います。502でどちらを疑うかは、この2段構成の理解で切り分けの速さが変わります。

入口で暗号化を終わらせる — 証明書を1箇所に集約する構成の詳細と副作用は [[HTTPS・TLS]]、中継する対象そのものの仕様は [[HTTP]] にあります。

関連技術とのつながり

  • [[HTTP]] / [[HTTPS・TLS]] — リバースプロキシが中継・終端するプロトコル
  • [[ロードバランサ]] — 複数サーバーへの振り分けという役割が重なる発展形
  • [[Docker]] — コンテナ構成ではNginxコンテナを入口に置く構成が定番
Q: リバースプロキシを挟むと、そのままではアプリサーバーのアクセスログに記録される送信元IPアドレスはどれ?
- [x] リバースプロキシのIPアドレス
- [ ] クライアントのIPアドレス
- [ ] DNSサーバーのIPアドレス
解説: 転送されるリクエストはプロキシが作り直したものです。本来のクライアントIPは `X-Forwarded-For` ヘッダーで別途伝える必要があります。

Q: 「後段のアプリには繋がったが、時間内に応答が返らなかった」ことを示すのはどれ?
- [ ] 502 Bad Gateway
- [x] 504 Gateway Timeout
- [ ] 413(リクエストサイズ超過)
解説: 502は後段に繋がらないか壊れた応答が返った場合、504は繋がったうえで応答が時間内に返らなかった場合です。どちらもプロキシ自身は動いています。

Q: 1台のリバースプロキシが443番ポートだけを公開したまま複数のサービスへ振り分けられるのはなぜ?
- [ ] サービスごとに別のIPアドレスを利用者へ公開しているから
- [x] リクエストのホスト名やパスを見て転送先を決められるから
- [ ] DNSが転送先を自動で書き換えてくれるから
解説: `Host` ヘッダーやURLのパスを手がかりに振り分けるため、外から見える入口は1つのまま、アプリは公開しない内部ポートで待ち受けられます。