◀ 4.

nginx リバースプロキシ設定 proxy_pass/upstream の書き方 | ネットワーク入門

▶

設定を読む前に:TLS終端・Hostと転送ヘッダーの信頼・障害時の継続は別々に確認します。旧コードは比較用に保持し、直前の訂正と新しい隔離例を区別しています。

同じ/api/items要求でもproxy_passのURIの有無で転送パスが変わる

この記事の要点
  • nginx をリバースプロキシにする中心ディレクティブは proxy_pass で、location で受けた要求を後ろのバックエンドへ転送する
  • 複数バックエンドへ分散する場合は upstream ブロックでサーバ群をまとめ、proxy_pass で upstream 名を指定する
  • proxy_set_headerは値の付与・上書き。利用者の自己申告値と信頼する入口で生成した情報を区別する
  • location でパスごとに転送先を変えられる。/api はアプリ、/ は静的配信、のような振り分けが定番
  • 入口のTLS終端とバックエンドへの暗号化・証明書検証は別。HTTP平文区間を安全とみなさない

概要

本記事では、リバースプロキシ の代表実装である nginx を、実際にリバースプロキシとして設定する方法を具体的なディレクティブとともに解説します。中心となるのは proxy_pass・upstream・location・proxy_set_header の 4 つです。これらを組み合わせると、SSL 終端・負荷分散・パスベースルーティングを 1 つの設定ファイルで実現できます。設定ファイルの基礎は conf(.conf) も参照してください。親項目は 負荷分散・プロキシ・CDN です。

仕組み

nginx の設定は http ブロックの中に server ブロックを置き、その中に location ブロックを並べる入れ子構造です。リバースプロキシに関わる主要ディレクティブは次のとおりです。

  • proxy_pass: その location にマッチした要求の転送先 (バックエンドの URL や upstream 名) を指定する。リバースプロキシの心臓部。
  • upstream: 複数のバックエンドサーバを 1 つのグループ名にまとめる。分散アルゴリズム (least_conn / ip_hash 等) もここで指定する。
  • location: パス (URL) ごとに処理を分ける。/api と / で別のバックエンドへ送るといった振り分けに使う。
  • proxy_set_header: バックエンドへ転送する際にヘッダを追加・上書きする。公開Hostを保持するか後段Hostへ固定するか、信頼する中継元はどこかを先に決める。既定のHostは$proxy_hostで、$hostへの変更が常に正解ではない。

とくに proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; は、受信X-Forwarded-Forに接続元IPを追記します。先頭に偽装値が残るため、そのままで元IPを正しく認証できるわけではありません。X-Forwarded-Proto $scheme は、このnginxへの接続方式を伝えます。前段でTLS終端している多段構成では元の利用者の方式とは限りません。HTTPS の基礎は HTTPS を参照してください。

設定・実用例

HTTPS で受け、/api/ はアプリ群へ負荷分散、それ以外はローカルの静的ファイルを返す、旧構成例を保存しています。HTTP (80) はHTTPSへリダイレクトしますが、未知Host拒否や後段の保護まで揃った完成設定ではありません。

保存した旧例への注意:旧HTTPS例の訂正:proxy_pass http://api_backend は平文で、/api/を保持します。$hostは許可Hostの保証ではなく、このserverが既定なら未知Hostも入り得ます。$proxy_add_x_forwarded_forは偽装値を残し、$schemeはこのnginxへの方式だけです。proxy_read_timeout 60sは読み取り間隔で総時間ではありません。backupはleast_connと併用できますが、ip_hashへ単純変更はできません。証明書ファイルは別途必要です。

# 複数のアプリサーバをまとめる
upstream api_backend {
    least_conn;
    server 10.0.0.21:8000;
    server 10.0.0.22:8000;
    server 10.0.0.23:8000 backup;   # 通常は使わない予備機
}

# HTTP は HTTPS へリダイレクト
server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

# HTTPS で受けて SSL 終端
server {
    listen 443 ssl;
    server_name www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    # /api/ への要求はアプリ群へ転送
    location /api/ {
        proxy_pass http://api_backend;

        # クライアントの本来の情報を引き継ぐ (定番セット)
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 60s;
    }

    # それ以外は静的ファイルを直接配信
    location / {
        root  /var/www/html;
        index index.html;
    }
}

設定変更後は同じバイナリ・設定パス・prefixを指定して構文チェックし、対象masterへ再読み込みを依頼します。gracefulなworker交代は無停止の無条件保証ではありません。

保存した旧例への注意:旧reload例の訂正:「問題なければ無停止」は保証ではありません。sudoやsystemctlは全配布環境の共通手順ではなく、別設定のnginx -t成功を稼働masterの検査と扱わないでください。対象バイナリ・-c/-p・PID・権限を照合してから、配布元の管理方法を1つだけ使い、error logと反映後のHTTPを確認します。今回ホストのサービス操作は行っていません。

# 構文チェック
sudo nginx -t

# 問題なければ無停止で再読み込み
sudo nginx -s reload
# もしくは
sudo systemctl reload nginx

主な用途

  • SSL 終端 + 平文転送: 入口証明書を集約する。バックエンドHTTPの通信保護は別途判断し、別ホストへのHTTPSでは証明書検証も有効化する。
  • 負荷分散: upstream に複数台並べ、proxy_pass でグループへ振り分ける。
  • パスベースルーティング: /api はアプリ、/static は静的配信、と location で分ける。
  • WebSocket 中継: Upgrade/Connection ヘッダを設定して双方向通信を通す。
  • キャッシュ前段: proxy_cache でバックエンドの応答をキャッシュし負荷を下げる。

主要ディレクティブの整理

ディレクティブ役割記述例
proxy_pass転送先の指定proxy_pass http://api_backend;
upstreamバックエンド群の定義upstream api_backend { ... }
locationパスごとの振り分けlocation /api/ { ... }
proxy_set_headerヘッダの付与/上書きproxy_set_header Host $host;
ssl_certificateSSL 終端の証明書ssl_certificate /path/crt;

注意点

  • proxy_pass の末尾スラッシュ: proxy_pass http://backend/; と / 有無で location のパス処理が変わる。意図しないパス書き換えに注意。
  • Host ヘッダの引き継ぎ: proxy_set_header Host $host; を忘れると、バックエンドが想定したホスト名で動作せず 404 やリダイレクトループになることがある。
  • X-Forwarded-For の信頼: 外部から直接届く位置のヘッダはクライアントが詐称できる。アプリ側は信頼するプロキシの値だけ採用する。
  • タイムアウト: proxy_read_timeoutは連続する読み取り操作の間隔で、処理全体の上限ではない。単純な延長前に無応答区間・buffering・アプリ側の期限を確認する。
  • reload 前に nginx -t: masterは新設定の適用に失敗すると旧設定で継続するが、アプリ到達性の成功までは検査しない。必ず nginx -t で検証する。

末尾スラッシュとヘッダーを隔離して確認する

訂正例AはHTTP loopback専用です。未知Hostを400にし、/api/のprefixを保持して後段へ渡し、外部の転送情報を入口で上書きします。固定Hostはこの単一アプリの契約に合わせた値で、すべてのサービスへそのまま流用する値ではありません。公開TLSとアプリ側の信頼設定は別途必要です。

# http context; isolated HTTP laboratory, not an Internet-facing TLS server.
upstream lab_app {
    server 127.0.0.1:18081;
}
server {
    listen 127.0.0.1:18080 default_server;
    server_name _;
    return 400;
}
server {
    listen 127.0.0.1:18080;
    server_name app.example.test;
    location /api/ {
        proxy_pass http://lab_app;
        proxy_http_version 1.1;
        proxy_set_header Host app.example.test;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host app.example.test;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Prefix "";
        proxy_set_header Forwarded "";
        proxy_connect_timeout 1s;
        proxy_read_timeout 2s;
        proxy_next_upstream off;
    }
    location / { return 404; }
}

訂正例Bではproxy_passにURI「/」を付けます。通常のprefix location /api/に対して、/api/items?x=1はAでは/api/items?x=1、Bでは/items?x=1になります。/api/の末尾スラッシュとproxy_passのURIを混同しないでください。

# http context; literal prefix locations, no regex/rewrite/variables.
server {
    listen 127.0.0.1:18082 default_server;
    server_name _;
    return 400;
}
server {
    listen 127.0.0.1:18082;
    server_name app.example.test;
    location /api/ {
        proxy_pass http://127.0.0.1:18081/;
        proxy_set_header Host app.example.test;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host app.example.test;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Prefix "";
        proxy_set_header Forwarded "";
        proxy_next_upstream off;
    }
}

この比較は変数やrewriteのない通常のprefix locationに限定します。正規表現・名前付きlocationでは置換範囲が決まらないためproxy_passにURIを指定できず、rewrite ... breakや変数入りproxy_passでは別の規則になります。/api自体のリダイレクトも確認し、パス・query・POST本文を実際のアプリ契約と照合します。

Hostと転送ヘッダーを選ぶ

既定Hostは$proxy_hostです。$hostは受信要求のHost等に由来する値で許可リストではなく、$http_hostはポートを含む受信値を保持します。公開Hostを維持するアプリと後段の仮想ホスト名が必要なアプリでは値が異なります。server_nameだけで未知Hostを排除できると思わずdefault_serverを設計し、redirect先も任意Hostから組み立てないようにします。

例Aは一段の入口です。前段CDN/LBがあるなら$remote_addrや$schemeは前段との接続情報になり、元の利用者情報とは限りません。信頼する前段IP・到達制限・real_ip設定とアプリのtrusted proxiesを揃え、Forwarded等の競合ヘッダーも扱います。proxy_set_headerは子階層に1つでも書くと親の同ディレクティブ群を引き継がないため、Hostだけ追加して他の上書きが消えないか確認します。

タイムアウトと再送の意味

proxy_read_timeout 2sなら、2秒以内に読み取りが続く応答は全体が2秒を超えても継続し得ます。逆に最初の応答まで無通信ならtimeoutになります。bufferingを切るだけで総時間制限になるわけでもありません。アプリの処理期限・クライアントの待ち時間・ログのupstream時間を分けて調べます。

例A/Bはproxy_next_upstream offで別候補への再送を止めます。LBを使うときも送信済みPOST等を再試行するnon_idempotentを安易に付けません。応答が不明でも上流では処理済みの場合があり、クライアント・アプリ側の再送を含めて冪等キーや結果照会が必要です。

reload前後に確認すること

nginx -tは構文や参照ファイル等を検査しますが、バックエンドの業務成功や容量を保証しません。reloadは新workerを起動して旧workerの接続終了を待つ仕組みです。長期接続、リソース不足、worker_shutdown_timeout、外部依存の障害も考慮し、適用後のerror log・新規要求・必要な既存接続を確認します。構文検査の成功や1回のHTTP成功を無停止の証明としません。

公式資料と検証範囲

2026年10月26日確認:nginxの転送・URI・ヘッダー・TLS検証・タイムアウトと再送、upstreamの失敗判定とbackup、受動/能動監視、商用health_check module、Hostとdefault server、信頼する中継元、reloadとworker交代、ApacheのHost保持。

新しいHTTP例はPC上の外部ネットワークなし・loopbackの専用nginxで検査するためのものです。公開TLS・実CDN・Apache・NGINX Plus・実アプリの認証や本番の無停止性は検証対象外です。設定・証明書・導入版・module・到達制限が異なる環境へ、試験結果をそのまま適用しないでください。

関連リンク

子ページ

子ページはありません

同階層のページ
  1. フォワードプロキシ
  2. リバースプロキシ
  3. ロードバランサ
  4. nginxリバースプロキシ設定
  5. HAProxy
  6. CDN

最近更新/作成されたページ