Nginx リバースプロキシ完全ガイド:upstream、バッファ、タイムアウト
午前3時。スマホが狂ったように振動している——本番環境アラート。
ログを確認すると、502 Bad Gatewayが大量発生。バックエンドサービスは落ちていない。しかし、Nginxのタイムアウト設定が短すぎた。トラフィック急増時、リクエスト処理完了前に強制切断されていた。あの proxy_read_timeout 60s の行を見て思った:適当に設定したのが仇になった。
その事故後、1週間かけてNginxリバースプロキシの3つの核心モジュールを徹底的に理解した:upstream負荷分散、proxy bufferバッファ、timeoutタイムアウト設定。正直、この3つが正しく設定できれば、リバースプロキシは10倍のトラフィックを処理できる。設定を間違えると、私みたいに午前3時のアラートで痛い目を見る。
この記事は、私が踏んだ罠、デバッグで学んだ経験、理解した原理を全部まとめたもの。バックエンド、運用やってる人、あるいは単にNginx設定パラメータの背後にあるロジックを理解したい人なら、これで時間を節約できるはず。
Upstream負荷分散:「リクエスト分散」以上の意味
まず基本構文を確認
upstream設定ブロックはNginx負荷分散の核心。基本的な書き方は見たことがあるでしょう:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}
server {
location / {
proxy_pass http://backend;
}
}
単純に見える——バックエンドサーバー群を定義、proxy_passで指向。しかし正直、これだけでは本番環境で不足。実際の運用では考慮すべき点が多い:サーバーが落ちたらどうする?性能の良いマシンに多くのトラフィックを割り振れる?長接続を維持する?
4つの負荷分散アルゴリズム、各シーンに適した選択
Nginxのデフォルトはラウンドロビン(round-robin)——順番に分配。公平だが、賢くない。
バックエンドが長接続シーン——WebSocket、DB接続プール——なら、ラウンドロビンで特定サーバーの接続数が急増する可能性。最小接続(least_conn)アルゴリズムが適切:
upstream backend {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
各サーバーのアクティブ接続数を追跡、新リクエストを最も空いているサーバーに送る。WebSocketでリアルタイムメッセージを push するプロジェクトで、ラウンドロビンだとあるサーバーのメモリが爆発——least_connに変更後、負荷分散が均一になった。
他のシーン:ユーザーがログイン後、以降のリクエストが同じサーバーに当たる必要がある(sessionがローカルに保存)。IP Hashがこれに対応:
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
同じクライアントIPのリクエストは固定バックエンドにハッシュされる。しかし正直、この方法には欠陥——そのサーバーが落ちるとsessionも消える。確実な方法はRedisでsession管理、ip_hashは一時的な対応。
4つ目は一意性ハッシュ(hash)、分散キャッシュシーンでよく使用:
upstream backend {
hash $request_uri consistent;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
Nginxは weight 単位あたり160個の仮想ノードを作成、リクエストURIをハッシュして特定サーバーに割り当て。メリット:キャッシュヒット率高——同じURIは常に同じマシンに当たる。
重み設定:マシン性能が異なる場合
バックエンドサーバーの性能が異なるのはよくある。32GB RAM、8コアCPUのマシンもあれば、16GB、4コアのみ。公平なラウンドロビン?高性能マシンが無駄になる。
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
weight=3のマシンは3倍のリクエストを受信。性能の良いマシンが多く処理、低性能マシンは少なく——合理的。
backupパラメータ、待機サーバー:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080 backup;
}
backupサーバーは通常の負荷分散に参加しない、プライマリ2台が落ちた時のみ有効化。ベンチ要員——スタメンが退場した時のみ出場。
Keepalive接続プール:性能倍増の秘密
ここは多くの人が見落とす。Nginxのデフォルト動作:リクエスト毎にバックエンドへのTCP接続を新規作成、レスポンス後に切断。問題ない?大きな問題。
TCP接続は3回ハンドシェイクで確立、4回ハンドシェイクで切断。高コンカレンシーシーン、このオーバーヘッドは恐ろしい。keepalive接続プールで接続を再利用、このコストを削減。
設定例:
upstream backend {
server 192.168.1.10:8080;
keepalive 32; # 各workerが32のidle接続を維持
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
2点注意:
keepalive 32は各workerプロセスが維持する最大idle接続数proxy_http_version 1.1とConnection ""の設定必須——HTTP/1.0は永続接続未対応
APIサービスでテスト——keepaliveなしでQPS約2000、有効化で4000以上。倍増は夸张ではなく、実測値。
しかしkeepalive値を高く設定すぎない。テスト環境で100に設定、バックエンドが1つのECSコンテナのみ——接続数で圧潰された。本番環境の計算式:
keepalive ≈ 総QPS × 平均レスポンス時間 ÷ workerプロセス数
例:予想QPS 10000、平均レスポンス50ms、4 worker:
10000 × 0.05 ÷ 4 = 125
keepaliveは125程度が合理的。
ヘルスチェック:落ちたら自動除外
Nginx OSS版はパッシブヘルスチェックのみ——リクエスト失敗後にサーバーを unhealthy マーク:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
}
proxy_next_upstreamは次サーバーへリトライする条件定義:接続エラー、タイムアウト、502/503/504。proxy_next_upstream_tries 3は最大3台試行。
しかしパッシブチェックには遅延——リクエスト失敗後にサーバー落ちを発見。可用性が重要なら、NGINX Plusのアクティブヘルスチェックが良い:
upstream backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
server {
location / {
proxy_pass http://backend;
health_check interval=5s fails=3 passes=2;
}
}
5秒毎にアクティブにヘルスチェックリクエスト送信。連続3回失敗で unhealthy マーク、連続2回成功で復帰。
Proxy Buffer:役立つ助手か、邪魔な存在か
バッファリング機構の役割
概念:Nginxはバックエンドからのレスポンスをクライアントに直接送らない——まずバッファに保存。
理由?クライアントのネット速度は予測不能。バックエンドは高速でデータを出力するが、クライアントが低速なら、Nginxは送信完了を待つ必要がある。バッファにより、Nginxはレスポンスを一度保存、ゆっくりクライアントへ送信——バックエンドは待機不要、次リクエストを早期処理可能。
しかしバッファリングには代償:メモリ消費。レスポンスが大きく、コンカレncyが高い場合、メモリ消費は無視できない。
3つの核心パラメータ、関係を理解
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
この3つは当初混乱した——名前が似て、意味が複雑。図を描いて理解:
proxy_buffer_size:レスポンスヘッダー用バッファ、リクエスト1つに1つproxy_buffers:レスポンスボディ用バッファ配列、形式は数 各サイズproxy_busy_buffers_size:クライアント送信中のバッファ部分、総バッファサイズの半分以下
例:proxy_buffers 8 32k、総サイズは8×32k = 256k。proxy_busy_buffers_size 64k、4分の1、ルールに従う。
何时調整?
バックエンドレスポンスヘッダーが巨大(多くのCookieなど)なら、“upstream sent too big header” エラー発生。解決:proxy_buffer_sizeを増大:
proxy_buffer_size 16k;
レスポンスボディが頻繁に大きい(大きなJSONなど)なら、バッファを増大:
proxy_buffers 16 64k;
特殊シーン:バッファを無効化
あるシーンでバッファは問題になる。
Server-Sent Events(SSE):バックエンドがイベントストリームを継続 push。Nginxがバッファすると、クライアントは遅延メッセージを受信。設定でバッファ無効化:
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
}
proxy_read_timeout 86400s(1日)はSSEが長接続、タイムアウト切断不可。
WebSocket:類似、双方向リアルタイム通信:
location /ws {
proxy_pass http://backend;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s;
}
大容量ファイルアップロード:クライアントが1GBアップロード、Nginxが全受信後にバックエンド転送なら、メモリ爆発。リクエストバッファ無効化:
location /upload {
proxy_pass http://backend;
proxy_request_buffering off;
client_max_body_size 1G;
}
proxy_request_buffering offでNginxは直接ストリーム転送——受信と転送を同時実行。
Timeout:設定値の背後にあるロジック
3つのタイムアウトパラメータ、各役割
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
名前が似て見える?役割は明確に異なる:
proxy_connect_timeout:NginxがTCP接続確立を待つ時間。バックエンドが遅い(ネット拥堵、防火墙)なら、超過で中断。proxy_read_timeout:接続後、Nginxがバックエンドデータを待つ時間。2回読取操作の間隔がこれを超えるとタイムアウト。proxy_send_timeout:Nginxがリクエストボディをバックエンド送信する時間制限。
よくある混乱:proxy_read_timeoutは総タイムアウトではない——読取間隔。バックエンドが5分処理するが、処理中にデータを継続送信(ハートビートなど)なら、proxy_read_timeout 60sで足る。しかしバックエンドが5分完全沈黙なら、proxy_read_timeout 300sが必要。
タイムアウトと502/504の関係
午前3時のアラートで学んだ重要教训:
- 502 Bad Gateway:Nginxがバックエンドに接続不能——サービスダウン、ポート不通、防火墙遮断
- 504 Gateway Timeout:Nginxは接続したが、バックエンド処理が長すぎてレスポンスなし
例:proxy_connect_timeout 10s、バックエンドが接続要求に15秒応答なら、Nginxは502返却。しかし接続が迅速確立、バックエンドが2分処理後にデータ返却、proxy_read_timeout 60sなら、504返却。
シーン別タイムアウト戦略
APIサービス:通常30-60秒で足る。APIは迅速レスポンスが期待——短タイムアウトで遅リクエストを発見:
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
ファイル処理:レポート出力、PDF生成は数分要する。タイムアウトを緩和:
proxy_connect_timeout 10s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
ストリーミングサービス:動画、WebSocket、SSE——長接続、1日設定も正常:
proxy_read_timeout 86400s;
502/504トラブルシューティング実践
原因分析
経験したケース:
- バックエンドサービス実際ダウン:プロセス崩壊、ポート占有、OOM
- 接続数枯渇:バックエンド接続プール満杯、Nginx接続不能
- タイムアウト設定過短:午前3時の事例——proxy_read_timeout 60s、バックエンド処理2分要
- 防火墙/ネット問題:セキュリティグループルール未設定、iptables遮断
ログ診断方法
最初のステップ:error_log確認:
error_log /var/log/nginx/error.log warn;
よくあるエラー:
upstream timed out (110: Connection timed out) while reading response header from upstream
これは504、読取タイムアウト。
connect() failed (111: Connection refused) while connecting to upstream
これは502、接続拒否——バックエンド未 listen。
高度な手法:upstream状態を表示するカスタムログ形式:
log_format upstream_status '$status $upstream_status $upstream_response_time';
access_log /var/log/nginx/access.log upstream_status;
200 200, 200, 502 0.5, 1.2, 3.0のような出力——どのバックエンドが何のステータス、どのくらいの時間を返却したか明確表示。
典型的解決策
ケース1:バックエンド処理遅、頻繁504
解決:proxy_read_timeout増大、バックエンドが実際処理完了可能を確認。Nginxのみ調整不可——バックエンドタイムアウトも同期調整。
ケース2:接続拒否、502
解決:バックエンドプロセス稼働、ポート listen、防火墙ルール許可を確認。
netstat -tlnp | grep 8080
ps aux | grep your_app
ケース3:高コンカレncyで接続数枯渇
解決:バックエンド接続プール制限増大、あるいはNginx upstream keepalive有効化で接続作成オーバーヘッド削減。
パフォーマンス最適化ベストプラクティス
Worker設定
Nginxはマルチプロセスモデル。worker_processesでプロセス数設定、通常CPUコア数と等価:
worker_processes auto;
autoはCPUコア数を自動検出。8コアマシン = 8 workerプロセス。
worker_connectionsは各workerが処理可能な最大接続数:
events {
worker_connections 4096;
}
理論最大コンカレント接続数 = worker_processes × worker_connections。8コア × 4096 = 32768。しかし実際値はシステムファイル descriptor 制限に依存。
TCP最適化3点セット
sendfile on;
tcp_nopush on;
tcp_nodelay on;
この3つ組み合わせで性能を大幅向上:
sendfile on:カーネルレベルファイル転送、ユーザー空間バッファを回避tcp_nopush on:sendfileと組み合わせ、パケットを一括送信而非逐次tcp_nodelay on:小データパケット即時送信、バッファ充填を待機しない
静的ファイルサービスでテスト——この3つ有効化でスループット30%以上向上。
他の最適化
gzip圧縮:テキストレスポンス圧縮転送、帯域節約:
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1024;
ファイル descriptor 制限:高コンカレncyで不足可能。システム制限確認:
ulimit -n
1024のみなら、増大必要。/etc/security/limits.conf編集:
* soft nofile 65535
* hard nofile 65535
完全設定例
本番環境推奨設定テンプレート:
# 基本設定
worker_processes auto;
events {
worker_connections 4096;
multi_accept on;
}
http {
# TCP最適化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Keepalive
keepalive_timeout 30;
keepalive_requests 100;
# バッファ設定
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
# タイムアウト設定
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
# gzip
gzip on;
gzip_types text/plain text/css application/json;
upstream backend {
least_conn;
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 backup;
keepalive 32;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
# SSE専用設定
location /events {
proxy_pass http://backend;
proxy_buffering off;
proxy_read_timeout 86400s;
}
}
}
まとめ
多くを語ったが、核心は3点:
- upstream設定:負荷分散アルゴリズムを正しく選択、keepalive接続プール有効化、ヘルスチェック設定
- バッファ設定:3つのパラメータの関係を理解、特殊シーンでバッファ無効化
- タイムアウト設定:各パラメータが何を管理するか理解、シーン別に異なる戦略使用
午前3時の事故で学んだこと:Nginx設定はパラメータを埋めるだけではない。各パラメータの背後に設計ロジック——原理理解で罠を回避。
Nginx初心者なら、デフォルト設定から開始、問題発生時に調整——私みたいに適当に proxy_read_timeout 60s を本番に投入しない。既に罠を踏んだ経験があるなら、この記事で散らばった経験を体系化できるはず。
この記事を書き終えた時、現在の本番環境設定を確認——keepalive 32、proxy_read_timeout 120s、least_conn負荷分散。午前3時のアラートは再び発生していない。
FAQ
proxy_read_timeoutは総タイムアウト?読取間隔?
proxy_bufferingをいつ無効化する?
• Server-Sent Events(SSE):リアルタイム push、バッファでメッセージ遅延
• WebSocket:双方向リアルタイム通信、ストリーム転送必要
• 大容量ファイルアップロード:メモリ爆発回避、受信と転送を同時実行
keepalive値は何を設定?
502と504の違いは?
負荷分散アルゴリズム選択は?
• ラウンドロビン(デフォルト):ステートレスサービス、公平分配
• least_conn:長接続シーン(WebSocket、DB接続プール)
• ip_hash:セッション維持必要(一時対応、Redisが良い)
• hash:分散キャッシュ、ヒット率高
upstream sent too big headerの解決?
sendfile + tcp_nopush + tcp_nodelayが性能向上する理由?
7 min read · 公開日: 2026年3月30日 · 更新日: 2026年3月30日
関連記事
shadcn/uiとRadix:コンポーネントをカスタマイズする際のアクセシビリティ維持方法
shadcn/uiとRadix:コンポーネントをカスタマイズする際のアクセシビリティ維持方法
Tailwind パフォーマンス最適化:JIT、content設定、本番バンドルサイズ管理
Tailwind パフォーマンス最適化:JIT、content設定、本番バンドルサイズ管理
Dialog、Sheet、Popover:オーバーレイコンポーネントのアクセシビリティとフォーカス管理

コメント
GitHubアカウントでログインしてコメントできます