はい、承知いたしました。Nginxのproxy_pass
ディレクティブについて、初心者向けに徹底的に解説し、設定方法を詳細に説明する記事を作成します。約5000語を目標に、概念、利点、基本的な設定から高度なオプション、トラブルシューティングまでを網羅します。
以下が記事の内容です。
【入門】Nginx proxy_passの徹底解説と設定方法
はじめに:なぜNginxとproxy_pass
が重要なのか?
現代のウェブアプリケーションは、かつてのように単一のサーバー上で全てが完結することは稀です。フロントエンドとバックエンドが分かれていたり、複数のAPIサービスが連携したり、データベースやキャッシュサーバーが別途存在したりと、様々なコンポーネントが組み合わさって動作しています。
このような複雑な構成において、クライアントからのリクエストを受け取り、適切なバックエンドサーバーへ転送する役割を担うのが「リバースプロキシ」です。そして、そのリバースプロキシ機能を実現するために、Nginxで最も頻繁に使用されるディレクティブの一つが、今回徹底解説するproxy_pass
です。
Nginxは、高速かつ軽量なWebサーバーとしてだけでなく、高性能なリバースプロキシ、ロードバランサー、HTTPキャッシュ、SSL/TLSターミネーターとしても広く利用されています。特にトラフィックの多い大規模なシステムでは、Nginxをフロントエンドに配置し、その背後で様々なバックエンドサービスを動作させる構成が一般的です。
この記事では、「入門」としてNginxやproxy_pass
を初めて学ぶ方でも理解できるよう、その基本的な概念から始まり、なぜproxy_pass
が必要なのか、どのように設定するのか、よくある設定ミスとその解決策、そしてパフォーマンスや信頼性を向上させるための応用的な設定までを、豊富な例を交えながら徹底的に解説します。
この記事を読めば、あなたは以下のことができるようになります。
- リバースプロキシの概念と
proxy_pass
の役割を理解する。 - 基本的な
proxy_pass
設定を記述し、ウェブアプリケーションへのプロキシを設定する。 location
ブロックとの連携、特に末尾のスラッシュの重要性を理解し、意図したパスへの転送を実現する。- リクエストヘッダー(
Host
、X-Forwarded-For
など)がどのように扱われるかを知り、適切に設定する。 - キャッシュやバッファリングなど、パフォーマンス関連の設定を理解する。
- タイムアウト設定による信頼性向上策を知る。
upstream
ブロックを使ったロードバランシングの基本を学ぶ。- よくあるエラー(502 Bad Gateway, 504 Gateway Timeoutなど)の原因と対処法を知る。
さあ、Nginx proxy_pass
の世界へ踏み込みましょう。
1. リバースプロキシとは? proxy_pass
の役割
proxy_pass
を理解する前に、まずは「プロキシ」と「リバースプロキシ」の概念を整理しましょう。
1.1 プロキシ (Proxy)
プロキシは「代理人」という意味です。インターネットの世界では、クライアント(あなたのブラウザなど)とサーバーの間に立ち、クライアントの代わりにサーバーと通信を行うプログラムやサーバーを指します。
最も一般的なのは「フォワードプロキシ」です。これは、企業の内部ネットワークなどから外部インターネットへアクセスする際に利用されることが多いです。クライアントはプロキシサーバーに対してリクエストを送り、プロキシサーバーがインターネット上の目的のサーバーから情報を受け取り、クライアントに返します。
- 目的: アクセス制御(特定のサイトへのアクセス制限)、キャッシュによる表示高速化、ログ記録、匿名性確保など。
- 向き: クライアント側の代理。
1.2 リバースプロキシ (Reverse Proxy)
リバースプロキシは、フォワードプロキシとは逆向きの代理人です。複数のバックエンドサーバー(アプリケーションサーバー、APIサーバーなど)の手前に立ち、クライアントからのリクエストを代表して受け取ります。そして、受け取ったリクエストを適切なバックエンドサーバーに転送し、バックエンドからの応答をクライアントに返します。
クライアントはリバースプロキシサーバーとだけ通信しているように見え、背後にある実際のバックエンドサーバーの存在や構成を知る必要はありません。
-
目的:
- 負荷分散 (Load Balancing): 複数のバックエンドサーバーにリクエストを分散させ、特定のサーバーへの負荷集中を防ぐ。
- セキュリティ強化: バックエンドサーバーを外部ネットワークから直接アクセスできないように隠し、攻撃の窓口を減らす。SSL/TLS終端を行い、バックエンドサーバーの負担を減らす。
- 静的コンテンツの高速配信: Nginx自身が得意とする静的ファイル(HTML, CSS, JS, 画像など)の配信を高速に行い、動的な処理を行うバックエンドサーバーの負担を軽減する。
- キャッシュ: バックエンドからの応答をキャッシュし、同じリクエストに対してはキャッシュから高速に応答を返す。
- SSL/TLS終端 (SSL Termination): クライアントとのSSL/TLS通信をリバースプロキシで行い、バックエンドサーバーは暗号化/復号化の処理から解放される。
- URLルーティング: 特定のパスに来たリクエストを特定のバックエンドサーバーに振り分ける。
- マイクロサービス: 複数のマイクロサービスへの入り口として機能し、クライアントからは単一のエンドポイントに見せる。
-
向き: サーバー側の代理。
Nginxはリバースプロキシとして非常に優れており、これらの利点を享受するために広く利用されています。
1.3 proxy_pass
ディレクティブの役割
proxy_pass
は、Nginxの設定ファイル(nginx.conf
やそのインクルードファイル)のlocation
ブロックの中で使用されるディレクティブです。その役割はただ一つ、「このlocation
ブロックにマッチしたリクエストを、指定されたバックエンドサーバーに転送せよ」とNginxに指示することです。
例えば、「/api/
で始まるURLへのリクエストは、http://backend_api_server
というアドレスで動作しているバックエンドサーバーに転送する」といった設定を行う際にproxy_pass
を使用します。
つまり、proxy_pass
はNginxをリバースプロキシとして機能させるための核心となるディレクティブなのです。
2. proxy_pass
の基本的な設定方法
proxy_pass
は、Nginxの設定ファイルの中でserver
ブロック内のlocation
ブロック内に記述します。
基本的な構造は以下のようになります。
“`nginx
http {
server {
listen 80; # リッスンするポート
location /some/path/ {
proxy_pass http://backend_server_address; # ここでproxy_passを使用
# その他のproxy_ ディレクティブ...
}
# 他のlocationブロックや設定...
}
# 他のserverブロックや設定...
}
“`
2.1 location
ブロックとの連携
proxy_pass
は常にlocation
ブロックの中で使用されます。location
ブロックは、受信したリクエストのURI(URLのパス部分)に基づいて、そのリクエストをどのように処理するかを定義します。proxy_pass
は、その処理の一つとして「外部サーバーへの転送」を指示します。
location
ディレクティブには、いくつかのマッチング方法があります。
location /
: 全てのリクエストにマッチする(最も一般的なデフォルト)。location = /exact/path
: 指定されたパスと完全に一致する場合のみマッチする。location /prefix/
: 指定されたプレフィックス(前方一致)にマッチする場合にマッチする。location ~ pattern
: 大文字小文字を区別する正規表現にマッチする場合にマッチする。location ~* pattern
: 大文字小文字を区別しない正規表現にマッチする場合にマッチする。
proxy_pass
の設定は、このlocation
ブロックのマッチ方法と、proxy_pass
で指定するURLの形式によって、バックエンドに転送されるリクエストのURIがどのように変化するかが変わります。これがproxy_pass
を理解する上で最も重要なポイントの一つです。
2.2 proxy_pass
のターゲット指定方法
proxy_pass
ディレクティブで指定できるターゲットは、主に以下の2つの形式があります。
- URLとして指定:
http://host[:port]
またはhttps://host[:port]
またはhttp://unix:/path/to/socket
の形式で、プロトコル、ホスト名(またはIPアドレス)、オプションでポート番号を指定します。この形式で指定する場合、末尾にパスを含めることもできます(例:http://backend/some/path/
)。 - Upstreamブロック名として指定: 後述する
upstream
ブロックで定義したサーバーグループの名前を指定します。ロードバランシングを行う場合に利用します。
例:
proxy_pass http://192.168.1.100:8080;
(IPアドレスとポートを指定)proxy_pass http://backend.local;
(ホスト名を指定。DNSで解決されるか、/etc/hosts
などに定義されている必要がある)proxy_pass http://unix:/var/run/backend.sock;
(Unixドメインソケットを指定)proxy_pass my_backend_group;
(Upstreamブロック名を指定)
3. proxy_pass
とURI変換のルール(末尾スラッシュの重要性)
proxy_pass
で最も混乱しやすいのが、オリジナルのリクエストURIが、バックエンドに転送される際にどのように書き換えられるかというルールです。これは、location
ブロックの定義方法と、proxy_pass
で指定するターゲットのURLにパスが含まれているか、そして末尾にスラッシュがあるかによって挙動が変わります。
基本的なルールは以下の通りです。
-
ルール A:
proxy_pass
のURLにパス部分が指定されている場合location
ブロックでマッチしたURIの部分が、proxy_pass
で指定されたパス部分に置き換えられます。- ただし、これは
location
ブロックが正規表現を使用していない場合(プレフィックスマッチや完全一致)に適用されます。 - 正規表現を使用している場合は、マッチしたURI全体がそのまま転送先に付加されます(後述のルールCに近い挙動)。
-
ルール B:
proxy_pass
のURLにパス部分が指定されていない場合(http://host;
またはhttp://host/;
の形式)location
ブロックがプレフィックスマッチ(例:location /prefix/
)の場合、マッチしたプレフィックス部分を除いたURIの残りの部分が、転送先のURLに付加されます。location
ブロックが完全一致(例:location = /exact
)の場合、マッチしたURI全体がそのまま転送先に付加されます。location
ブロックが正規表現の場合、マッチしたURI全体がそのまま転送先に付加されます(後述のルールC)。
-
ルール C:
location
ブロックが正規表現の場合proxy_pass
で指定されたURLにパスが含まれていても、そのパスは無視されます。- オリジナルのリクエストURI全体(クエリ文字列は含まない)が、転送先のURLに付加されます。
これらのルールを、具体的な例で見ていきましょう。
以下のNginx設定があるとします。
“`nginx
server {
listen 80;
# 例 1: locationとproxy_passの両方にパスがあり、proxy_passに末尾スラッシュ
location /app1/ {
proxy_pass http://backend1/newpath/;
}
# 例 2: locationとproxy_passの両方にパスがあり、proxy_passに末尾スラッシュなし
location /app2/ {
proxy_pass http://backend2/newpath; # 注意: 末尾にスラッシュがない
}
# 例 3: locationにパスがあり、proxy_passにはパスがない(末尾スラッシュあり)
location /app3/ {
proxy_pass http://backend3/;
}
# 例 4: locationにパスがあり、proxy_passにはパスがない(末尾スラッシュなし)
location /app4/ {
proxy_pass http://backend4; # 注意: 末尾にスラッシュがない
}
# 例 5: locationがルートパス、proxy_passにパスがない(末尾スラッシュあり)
location / {
proxy_pass http://backend5/;
}
# 例 6: locationがルートパス、proxy_passにパスがない(末尾スラッシュなし)
location / { # 同じlocation / だが、proxy_passが異なる
proxy_pass http://backend6;
}
# 例 7: locationが完全一致、proxy_passにパスがない(末尾スラッシュなし)
location = /exact {
proxy_pass http://backend7;
}
# 例 8: locationが正規表現
location ~ ^/api/(.*)$ {
proxy_pass http://backend8; # proxy_passにパスがあっても無視される (例: http://backend8/v1/ としても結果は同じ)
}
# 例 9: locationが正規表現で、proxy_passにパスがある (パスは無視される)
location ~ ^/api_v2/(.*)$ {
proxy_pass http://backend9/ignored_path/; # このパス部分は無視される
}
}
“`
クライアントから http://yourdomain.com/appX/resource/page
というリクエストが来た場合、それぞれのlocation
ブロックでマッチした後の転送先URIは以下のようになります。
-
例 1:
location /app1/
とproxy_pass http://backend1/newpath/;
- リクエストURI:
/app1/resource/page
location /app1/
にマッチ。マッチした部分/app1/
は取り除かれる。残りはresource/page
。proxy_pass
のURLにパス部分/newpath/
が指定されている。残りの部分resource/page
が、この/newpath/
の末尾に付加される。- 転送先URI:
http://backend1/newpath/resource/page
- リクエストURI:
-
例 2:
location /app2/
とproxy_pass http://backend2/newpath;
- リクエストURI:
/app2/resource/page
location /app2/
にマッチ。マッチした部分/app2/
は取り除かれる。残りはresource/page
。proxy_pass
のURLにパス部分/newpath
が指定されている。残りの部分resource/page
が、この/newpath
の末尾に付加される。- 転送先URI:
http://backend2/newpathresource/page
- 注目:
newpath
の後ろにスラッシュがないため、resource
が直接結合されてしまいます。これは意図しない挙動となることが多いです。
- リクエストURI:
-
例 3:
location /app3/
とproxy_pass http://backend3/;
- リクエストURI:
/app3/resource/page
location /app3/
にマッチ。マッチした部分/app3/
は取り除かれる。残りはresource/page
。proxy_pass
のURLにパス部分/
が指定されている。残りの部分resource/page
が、この/
の末尾に付加される。- 転送先URI:
http://backend3/resource/page
- 最も一般的で推奨されるパターン:
location /prefix/
とproxy_pass http://backend/;
の組み合わせは、/prefix/
以下のパス構造をそのままバックエンドのルートパス以下にマッピングしたい場合に適しています。
- リクエストURI:
-
例 4:
location /app4/
とproxy_pass http://backend4;
- リクエストURI:
/app4/resource/page
location /app4/
にマッチ。proxy_pass
のURLにパス部分が指定されていない。- オリジナルのURI
/app4/resource/page
全体が、転送先のURLに付加される。 - 転送先URI:
http://backend4/app4/resource/page
- 注目:
location
に末尾スラッシュがあるのに、proxy_pass
にパスがない場合、location
でマッチしたパス/app4/
がそのままバックエンドに渡されます。
- リクエストURI:
-
例 5:
location /
とproxy_pass http://backend5/;
- リクエストURI:
/any/path/
location /
にマッチ。マッチした部分/
は取り除かれる(ルートパスのみ)。残りはany/path/
。proxy_pass
のURLにパス部分/
が指定されている。残りの部分any/path/
が、この/
の末尾に付加される。- 転送先URI:
http://backend5/any/path/
- 全てのトラフィックをバックエンドのルート以下にマッピングする標準パターン。
- リクエストURI:
-
例 6:
location /
とproxy_pass http://backend6;
- リクエストURI:
/any/path/
location /
にマッチ。proxy_pass
のURLにパス部分が指定されていない。- オリジナルのURI
/any/path/
全体が、転送先のURLに付加される。 - 転送先URI:
http://backend6/any/path/
- 例 5と同じ結果になる:
location /
の場合は、proxy_pass http://host;
とproxy_pass http://host/;
は同じ挙動になります。これは、location /
でマッチするURIの「残りの部分」が常にオリジナルのURI全体と同じになるためです。
- リクエストURI:
-
例 7:
location = /exact
とproxy_pass http://backend7;
- リクエストURI:
/exact
location = /exact
に完全に一致。proxy_pass
のURLにパス部分が指定されていない。- オリジナルのURI
/exact
全体が、転送先のURLに付加される。 - 転送先URI:
http://backend7/exact
- リクエストURI:
-
例 8:
location ~ ^/api/(.*)$
とproxy_pass http://backend8;
- リクエストURI:
/api/users/123
location ~ ^/api/(.*)$
にマッチ。正規表現を使用している。proxy_pass
のURLにパス部分がない。- オリジナルのURI
/api/users/123
全体が、転送先のURLに付加される。 - 転送先URI:
http://backend8/api/users/123
- 注意: 正規表現の場合、
proxy_pass http://backend8/v1/;
のようにパスを指定しても、そのパスは無視され、常にオリジナルURI全体が付加されます(例 9参照)。URI変換を制御したい場合は、rewrite
ディレクティブと組み合わせる必要があります。
- リクエストURI:
-
例 9:
location ~ ^/api_v2/(.*)$
とproxy_pass http://backend9/ignored_path/;
- リクエストURI:
/api_v2/products/abc
location ~ ^/api_v2/(.*)$
にマッチ。正規表現を使用している。proxy_pass
のURLにパス部分/ignored_path/
が指定されているが、正規表現のlocation
のため無視される。- オリジナルのURI
/api_v2/products/abc
全体が、転送先のURLに付加される。 - 転送先URI:
http://backend9/api_v2/products/abc
- リクエストURI:
これらの例からわかる最も重要なポイントは、location /prefix/ { proxy_pass http://backend; }
のように、location
に末尾スラッシュがあるのにproxy_pass
にパスがない形式は、意図しないURI(オリジナルのパス全体が付加される)になる可能性が高いため、避けるべきであるということです。
推奨される安全なパターンは以下の2つです。
location /prefix/ { proxy_pass http://backend/; }
:/prefix/
以下をバックエンドのルート(/
)以下にマッピングする場合。location /prefix { proxy_pass http://backend/newpath; }
:/prefix
をバックエンドの/newpath
にマッピングし、/prefix/suffix
を/newpathsuffix
にマッピングする場合。(ただし、proxy_pass
のパスに末尾スラッシュがない場合、パス結合に注意が必要)location ~ ^/prefix/(.*)$ { proxy_pass http://backend/; }
またはlocation ~ ^/prefix/(.*)$ { proxy_pass http://backend; }
: 正規表現の場合。オリジナルのURI全体がバックエンドに渡される。URI変換が必要な場合はrewrite
を併用。
特に、location
がパスプレフィックスで、proxy_pass
のターゲットがホスト名(+ポート)のみの場合は、proxy_pass http://backend/;
のように末尾にスラッシュを付けることを強く推奨します。これにより、location
でマッチしたパス部分が取り除かれ、残りのパスがバックエンドのルート (/
) から始まる形で転送されるという、直感的で扱いやすい挙動になります。
4. リクエストヘッダーの処理 (proxy_set_header
)
クライアントからNginxに送られてきたリクエストには、様々なヘッダーが含まれています(例: Host
, User-Agent
, Referer
, Cookie
など)。Nginxがこのリクエストをバックエンドサーバーにプロキシする際、これらのヘッダーはデフォルトでどのように扱われるのでしょうか?また、バックエンドサーバーが必要とする情報をNginxが追加するにはどうすれば良いのでしょうか?
4.1 デフォルトのヘッダー処理
Nginxがリクエストをプロキシする際、クライアントから受け取ったオリジナルのヘッダーの一部はそのままバックエンドに転送されますが、いくつか重要なヘッダーはNginxによって変更されたり、新しく追加されたりします。
特に、以下のヘッダーはデフォルトでNginxが設定します。
Host
:proxy_pass
で指定したバックエンドのホスト名(またはIPアドレス)とポートになります。オリジナルのHost
ヘッダー(クライアントがNginxに送ったヘッダー)はデフォルトではバックエンドに渡されません。Connection
:close
に設定されます。クライアントとNginxの間、およびNginxとバックエンドの間で、コネクションの管理をNginxが行うためです。- その他の大部分のヘッダー(
User-Agent
,Referer
,Cookie
など)は、デフォルトではそのままバックエンドに転送されます。
4.2 バックエンドが必要とするヘッダー (X-Forwarded-*
)
バックエンドサーバーは、リクエストを処理する際に、オリジナルのクライアントに関する情報が必要となることがよくあります。しかし、クライアントはNginxと直接通信しているため、バックエンドから見ると全てのリクエストがNginxから来ているように見えます。
この問題を解決するために、リバースプロキシでは一般的に以下のX-Forwarded-*
ヘッダーをリクエストに追加し、オリジナルのクライアント情報をバックエンドに伝達します。
X-Real-IP
: オリジナルのクライアントのIPアドレス。X-Forwarded-For
: オリジナルのクライアントのIPアドレス、および(途中に他のプロキシがある場合)そのプロキシのIPアドレスのリスト。X-Forwarded-Proto
: オリジナルのリクエストで使用されたプロトコル(http
またはhttps
)。X-Forwarded-Host
: オリジナルのリクエストでクライアントが指定したHost
ヘッダーの値。
これらのヘッダーは、バックエンドサーバーがクライアントのIPアドレスに基づいた処理(アクセス制限、ログ記録など)を行ったり、オリジナルのプロトコル(HTTPSでアクセスされたかなど)を判断したりするために非常に重要です。
4.3 proxy_set_header
ディレクティブ
これらのヘッダーを設定したり、オリジナルのHost
ヘッダーをバックエンドに渡したりするために使用するのが、proxy_set_header
ディレクティブです。
構文: proxy_set_header Header-Name Value;
Value
には、固定文字列の他に、Nginxが提供する変数を使用することができます。
最も一般的な設定例:
バックエンドがオリジナルのHost
ヘッダー、クライアントIP、およびプロトコルを知るために、通常以下の設定を行います。
“`nginx
location / {
proxy_pass http://backend_server;
proxy_set_header Host $host; # オリジナルのHostヘッダーを渡す
proxy_set_header X-Real-IP $remote_addr; # クライアントIPを渡す
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # X-Forwarded-Forを適切に設定
proxy_set_header X-Forwarded-Proto $scheme; # オリジナルのプロトコル (http/https) を渡す
}
“`
$host
: Nginxが受け取ったリクエストのHost
ヘッダーの値(またはサーバー名)。これにより、バックエンドは元のドメイン名を知ることができます。多くのアプリケーションフレームワークやWebサーバーは、このHost
ヘッダーを見て仮想ホストを切り替えたり、リダイレクトURLを生成したりするため、正しく設定することが非常に重要です。$remote_addr
: クライアントのIPアドレス。$proxy_add_x_forwarded_for
:$remote_addr
と、既存のX-Forwarded-For
ヘッダーの値(複数のプロキシを経由した場合)をカンマ区切りで連結した値です。これにより、リクエストが複数のプロキシを経由した場合でも、オリジナルのクライアントIPを含む全ての中継IPのリストがバックエンドに伝わります。$scheme
: リクエストのプロトコル(http
またはhttps
)。NginxでSSL終端を行っている場合、クライアントはNginxにはHTTPSで接続しますが、NginxからバックエンドへはHTTPで接続することがあります。このとき、バックエンドはX-Forwarded-Proto: https
ヘッダーを見て、オリジナルのリクエストがHTTPSだったことを知ることができます。
ヘッダーを削除する:
特定のヘッダーをバックエンドに転送したくない場合は、proxy_set_header
の値を空文字列にすることで削除できます。
nginx
location / {
proxy_pass http://backend;
proxy_set_header User-Agent ""; # User-Agentヘッダーを削除
}
proxy_set_header
ディレクティブは、同じヘッダー名に対して複数回指定された場合、最後の設定が優先されます。また、http
, server
, location
の各レベルで指定可能で、よりネストされたレベルの設定が外側のレベルの設定を上書きします。
一般的には、前述の最も一般的な設定例(Host
, X-Real-IP
, X-Forwarded-For
, X-Forwarded-Proto
の設定)をserver
ブロックやlocation
ブロックの先頭に記述するのが良いプラクティスです。
5. レスポンスバッファリング (proxy_buffering
)
Nginxがバックエンドサーバーから応答を受け取った後、それをすぐにクライアントに送信するか、あるいは一旦Nginxの内部バッファ(メモリ上またはディスク上)に貯めてからクライアントに送信するかを制御するのが、レスポンスバッファリングです。proxy_buffering
ディレクティブで制御します。
proxy_buffering on;
(デフォルト): Nginxはバックエンドからの応答をバッファに貯めます。バックエンドからの応答が完全に受信されるか、バッファが満杯になるまで、応答データをクライアントに送信しません。proxy_buffering off;
: Nginxはバックエンドから応答を受信次第、逐次クライアントに送信します。
5.1 バッファリングが有効な場合 (on
) の利点と欠点
利点:
- パフォーマンス: バックエンドは応答データを一気にNginxに送ってコネクションを閉じることができるため、バックエンドのリソースをすぐに解放できます。Nginxは、その高速なディスクI/Oやネットワーク処理能力を活かして、データをゆっくり応答を受け取る遅いクライアントに対しても効率的にデータを送信できます。
- クライアントの速度に左右されない: バックエンドはクライアントの接続速度を気にせず、最大速度でNginxにデータを送れます。Nginxがクライアントへの送信速度を調整します。
- キャッシュ: バッファリングが有効でないと、応答全体をNginxが把握できないため、
proxy_cache
機能を利用できません。 - Gzip圧縮: Nginxで
gzip on;
などが設定されている場合、バッファリングされた応答全体に対して圧縮を適用できます。バッファリングが無効だと、チャンクごとに圧縮する必要があり効率が悪くなるか、圧縮が適用できない場合があります。 - より安定した接続: 一旦バッファに貯めることで、バックエンドの一時的な切断などがあっても、Nginxが応答を保持している限りクライアントへの送信を継続できます。
欠点:
- 初回バイトまでの遅延: バックエンドからの応答が完全にバッファに貯まるまで(あるいはバッファの一部が満杯になるまで)、クライアントは最初のバイトを受け取れません。特に大きな応答の場合、体感速度が遅く感じられる可能性があります。
- リソース消費: バッファのためにメモリやディスク容量を消費します。
5.2 バッファリングが無効な場合 (off
) の利点と欠点
利点:
- 応答性の向上: バックエンドからの応答を逐次クライアントに送信するため、クライアントはすぐに最初のバイトを受け取ることができ、体感速度が向上する場合があります。
- リソース消費の抑制: バッファリングのためのメモリやディスクをほとんど使用しません。
- ストリーミング: リアルタイム性の高いデータストリーム(例: Server-Sent Events, WebSocketsなど)を扱う場合に適しています。ただし、WebSocketの場合は
proxy_buffering
は影響しません(専用のハンドシェイクとプロトコルがあるため)。
欠点:
- バックエンドのリソース消費: バックエンドサーバーは、遅いクライアントのためにコネクションを長時間保持する必要が出てくる可能性があります。
- キャッシュやGzip圧縮の制限:
proxy_cache
が利用できず、gzip
圧縮も効率が悪くなるか適用できない場合があります。 - 信頼性の低下: バックエンドが途中で切断された場合、クライアントへの送信もそこで止まってしまいます。
5.3 関連ディレクティブ
バッファリングを有効にする場合、関連する以下のディレクティブでバッファのサイズなどを調整できます。
proxy_buffer_size
: 各リクエストに対する最初のバッファのサイズ。通常、バックエンドからの応答ヘッダーを格納できるサイズに設定します。(デフォルト: 4k または 8k)proxy_buffers
: レスポンスボディを格納するためのバッファの数とサイズ。(デフォルト: 8 4k/8k) 例:proxy_buffers 8 16k;
(16KBのバッファを8つ)proxy_busy_buffers_size
: クライアントに送信中であっても、まだバックエンドからの読み込みに使用できるバッファの最大サイズ。proxy_temp_path
: バッファリングされた応答が、メモリバッファに収まらずディスクに書き込まれる場合に、その一時ファイルを保存するディレクトリ。proxy_temp_file_write_size
: ディスクに一時ファイルを書き込む際の、一度に書き込むデータの最大サイズ。
通常はデフォルト設定で十分なことが多いですが、大きな応答を扱う場合や特定のエラー(例: “an upstream response is too large to buffer”)が出る場合は、これらの値を調整する必要があるかもしれません。
推奨: 特段の理由がない限り、proxy_buffering on;
(デフォルト) のまま利用し、Nginxの持つパフォーマンス最適化機能を最大限に活用することを推奨します。リアルタイムストリーミングなど、バッファリングが明確に不都合な場合のみ proxy_buffering off;
を検討してください。
6. キャッシュ (proxy_cache
)
Nginxをリバースプロキシとして利用する大きな利点の一つが、バックエンドからの応答をキャッシュする機能です。これにより、同じリクエストが来た際にバックエンドにアクセスすることなく、Nginxがキャッシュから高速に応答を返すことができます。バックエンドサーバーの負荷を劇的に減らし、応答速度を向上させることができます。
6.1 キャッシュの仕組み
- クライアントからNginxにリクエストが来る。
- Nginxは指定されたキャッシュキー(通常はリクエストURIなどに基づいて生成される)に対応するキャッシュエントリを検索する。
- キャッシュヒット: キャッシュエントリが見つかり、有効期限内であれば、Nginxはそのキャッシュされた応答をクライアントに返す。バックエンドにはアクセスしない。
- キャッシュミス: キャッシュエントリが見つからない、または有効期限切れの場合、Nginxはリクエストをバックエンドサーバーに転送する。
- バックエンドからの応答を受信したら、Nginxはその応答を指定された期間キャッシュに保存する。
- Nginxはその応答をクライアントに返す。
6.2 キャッシュの設定ステップ
キャッシュを設定するには、主に以下の2つのステップが必要です。
ステップ 1: キャッシュゾーンの定義 (proxy_cache_path
)
http
ブロックのトップレベルで、キャッシュデータが保存されるディスク上の場所と、キャッシュゾーンのパラメータを定義します。
“`nginx
http {
# … 他の設定 …
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
# ...
}
}
“`
/path/to/cache
: キャッシュファイルを保存するディスク上のパス。Nginxユーザーに書き込み権限が必要です。levels=1:2
: キャッシュキーに基づいてキャッシュファイルを格納するサブディレクトリの階層を指定します。例:levels=1:2
はc/29/a3c2c...
のような階層を作成します。これにより、単一のディレクトリに大量のファイルが作成されるのを防ぎ、パフォーマンスを向上させます。keys_zone=my_cache:10m
: キャッシュキーとメタデータ(ヘッダー、有効期限など)を保存するための共有メモリゾーンを定義します。my_cache
はこのゾーンの名前、10m
はそのサイズ(この例では10MB)です。このメモリゾーンは、キャッシュの検索を高速化し、キャッシュされたアイテムに関する情報を管理します。10MBで数万件のメタデータを格納できます。max_size=1g
: キャッシュゾーンのディスク使用量の最大サイズ(この例では1GB)です。このサイズを超えると、最も古いデータが削除されます。inactive=60m
: 指定された時間(この例では60分)アクセスされなかったキャッシュデータは削除されます。use_temp_path=off
: キャッシュファイル書き込み時の一時ファイルパスをproxy_cache_path
で指定したパスと同じにします。on
(デフォルト) にすると、proxy_temp_path
で指定されたパスが使用されます。一般的にはoff
が良いとされます。
ステップ 2: location
ブロックでのキャッシュ利用指定 (proxy_cache
)
location
ブロック内で、どのキャッシュゾーンを使用するかを指定します。
“`nginx
server {
listen 80;
location /static/ {
# 静的ファイルは有効期限を長くキャッシュ
proxy_cache my_cache; # 使用するキャッシュゾーンの名前
proxy_cache_valid 200 302 10m; # 成功 (200, 302) の応答を10分間キャッシュ
proxy_cache_valid 404 1m; # 404エラーも1分間キャッシュ
proxy_cache_key "$scheme$request_method$host$request_uri"; # キャッシュキーの定義
proxy_pass http://backend_static;
}
location /api/ {
# APIはキャッシュしない、または短時間キャッシュ
proxy_cache my_cache;
proxy_cache_valid 200 1m; # 成功応答を1分間だけキャッシュ (更新頻度が高い場合)
# proxy_cache_bypass $http_pragma $http_authorization; # 特定のヘッダーがある場合はキャッシュをバイパス
proxy_no_cache $cookie_nocache $arg_nocache; # 特定のcookieやクエリ文字列がある場合はキャッシュしない
proxy_pass http://backend_api;
}
location / {
# それ以外はキャッシュしない(または別途定義)
proxy_pass http://backend_app;
}
}
“`
proxy_cache my_cache;
: このlocation
ブロックに来たリクエストに対して、my_cache
という名前のキャッシュゾーンを使用することを指定します。proxy_cache_valid status [time];
: 指定されたHTTPステータスコードの応答を、指定された時間キャッシュすることを定義します。複数のステータスや時間を定義できます。proxy_cache_key string;
: キャッシュエントリを一意に識別するためのキーを定義します。変数を組み合わせて、リクエストメソッド、ホスト、URIなどを含むキーを生成するのが一般的です。デフォルトは$scheme$proxy_host$request_uri
です。proxy_cache_bypass string ...;
: 指定された変数(通常はヘッダーやクエリ文字列)が空でない場合に、キャッシュをチェックせずにバックエンドに直接リクエストを転送します。例: 認証ヘッダーがある場合はキャッシュをバイパスする。proxy_no_cache string ...;
: 指定された変数が空でない場合に、キャッシュを使用せず、また応答もキャッシュしないようにします。例:?nocache=true
のようなクエリ文字列がある場合にキャッシュしないようにする。
6.3 キャッシュに関するその他のディレクティブ
proxy_cache_methods method ...;
: キャッシュするHTTPメソッドを指定します(デフォルト: GET, HEAD)。proxy_cache_min_uses number;
: 指定された回数以上リクエストされたアイテムのみキャッシュします。proxy_cache_lock on;
: 複数のクライアントが同じキャッシュミスのアイテムを同時にリクエストした場合、最初の1つのリクエストだけをバックエンドに転送し、他のリクエストは最初の応答がキャッシュされるまで待機させます。これにより、バックエンドへの同時リクエストを減らせます。proxy_cache_revalidate on;
: キャッシュされた応答が期限切れの場合、If-Modified-Since
やIf-None-Match
ヘッダーをバックエンドに送信し、コンテンツが変更されていなければ304 Not Modified応答を受け取ってキャッシュを更新(有効期限を延長)します。
キャッシュは適切に設定すれば大きな効果を発揮しますが、キャッシュすべきでない応答(ユーザー固有の情報を含むページなど)をキャッシュしたり、キャッシュの有効期限を間違えたりすると、古い情報が表示されてしまう原因にもなります。キャッシュ戦略はアプリケーションの特性に合わせて慎重に設計する必要があります。
7. タイムアウト設定 (proxy_connect_timeout
, proxy_send_timeout
, proxy_read_timeout
)
リバースプロキシでは、Nginxとバックエンドサーバー間の通信におけるタイムアウト設定が非常に重要です。これらの設定が適切でないと、バックエンドの応答が遅い場合にクライアントにエラーを返してしまったり、Nginxのリソースが長時間占有されてしまったりします。
Nginxのproxy_pass
に関連する主なタイムアウト設定は以下の3つです。
-
proxy_connect_timeout time;
: NginxがバックエンドサーバーとのTCPコネクションを確立しようとする際の最大時間。この時間内にコネクションが確立できない場合、Nginxは通常502 Bad Gatewayエラーを返します。- デフォルト:
60s
- 設定例:
proxy_connect_timeout 5s;
(短く設定することで、応答しないサーバーへの接続試行を早期に打ち切る)
- デフォルト:
-
proxy_send_timeout time;
: Nginxがバックエンドサーバーにリクエストを送信する際、write操作の間に応答を待つ最大時間。バックエンドが指定された時間内にデータを受け付けない場合、Nginxはリクエスト送信を中止します。- デフォルト:
60s
- 設定例:
proxy_send_timeout 10s;
- デフォルト:
-
proxy_read_timeout time;
: Nginxがバックエンドサーバーからの応答を受信する際、read操作の間に応答を待つ最大時間。バックエンドが指定された時間内にデータ(応答ヘッダーやボディのチャンク)を送信しない場合、Nginxは応答の受信を中止します。これが最も頻繁に遭遇するタイムアウトで、バックエンドの処理が遅い場合に発生します。発生すると通常504 Gateway Timeoutエラーを返します。- デフォルト:
60s
- 設定例:
proxy_read_timeout 300s;
(バッチ処理など、応答に時間がかかるAPIのために長く設定する場合)
- デフォルト:
これらのタイムアウトは、http
, server
, location
の各レベルで設定できます。特定のlocation
ブロックでだけタイムアウトを変更したい場合は、そのlocation
ブロック内に記述します。
設定の考え方:
proxy_connect_timeout
: 通常は短めに設定します(例: 1秒〜10秒)。サーバーが生きていればすぐに接続できるはずなので、これに時間がかかる場合はサーバーがダウンしているかネットワークに問題がある可能性が高いです。proxy_send_timeout
: バックエンドがリクエストボディを受け付ける速度が遅い場合に調整が必要になることがありますが、一般的なGETリクエストなどではあまり問題にならないことが多いです。デフォルトで十分な場合が多いです。proxy_read_timeout
: バックエンドの処理時間に合わせて調整します。Webページ表示のような一般的なリクエストであればデフォルトの60秒で十分ですが、API呼び出しで複雑な計算や外部サービスとの連携など時間がかかる場合は、バックエンドの最大処理時間より少し長めに設定する必要があります。ただし、あまり長くしすぎると、問題のあるバックエンドがNginxのリソースを長時間占有してしまう可能性があるため注意が必要です。
これらのタイムアウト設定は、バックエンドの障害やパフォーマンス問題をクライアントに適切に(早めに)伝えるために重要です。例えば、バックエンドが完全にダウンしているのに接続タイムアウトが長いと、クライアントは長時間待たされることになります。逆に、バックエンドは正常でもNginxの読み込みタイムアウトが短いと、処理中のリクエストでもタイムアウトエラーになってしまいます。
8. エラーハンドリング (proxy_intercept_errors
)
バックエンドサーバーがHTTPエラー応答(4xxや5xx)を返した場合、Nginxはデフォルトではその応答をそのままクライアントに透過的に渡します。しかし、Nginxで独自のエラーページを表示したり、エラー処理をカスタマイズしたりしたい場合があります。
proxy_intercept_errors
ディレクティブは、Nginxがバックエンドからのエラー応答を「捕捉(intercept)」するかどうかを制御します。
proxy_intercept_errors off;
(デフォルト): バックエンドからのエラー応答をそのままクライアントに渡します。proxy_intercept_errors on;
: バックエンドからのエラー応答(4xxまたは5xx)をNginxが捕捉し、それに対する処理(例:error_page
ディレクティブで指定したページを表示)を行います。
設定例:
バックエンドが返す500番台のエラーをNginxが捕捉し、カスタムエラーページを表示する例です。
“`nginx
server {
listen 80;
location / {
proxy_pass http://backend_server;
proxy_intercept_errors on; # バックエンドからのエラーを捕捉
# 捕捉した500番台のエラーに対して、/50x.html を内部リダイレクト
error_page 500 502 503 504 /50x.html;
}
location = /50x.html {
# カスタムエラーページのパス
# ここで表示する静的なHTMLファイルを指定
root html; # Nginxのインストールディレクトリ内のhtmlディレクトリなど
internal; # 外部からの直接アクセスを禁止
}
}
“`
この設定により、バックエンドが例えば500 Internal Server Errorを返した場合、Nginxはその応答を捕捉し、/50x.html
への内部リクエストを発行します。そして、location = /50x.html
ブロックで定義された静的なHTMLファイル(例: html/50x.html
)の内容をクライアントに返します。これにより、クライアントにはNginxが用意した一貫性のあるエラー画面が表示されます。
error_page
ディレクティブは複数のステータスコードを指定したり、リダイレクト先として別のURLを指定したりすることも可能です。
9. SSL/TLSの設定
NginxはSSL/TLS終端(クライアントとのHTTPS通信をNginxで行い、バックエンドへはHTTPでプロキシする)の役割を果たすことが非常に多いです。また、Nginxからバックエンドへの通信をHTTPSで行う場合もあります。proxy_pass
はこれらのシナリオに対応しています。
9.1 SSL/TLS終端 (Nginx HTTPS -> Backend HTTP)
これは最も一般的なパターンです。NginxでクライアントとのHTTPS通信を処理し、バックエンドサーバーはHTTPで動作させます。バックエンドはSSL/TLSの負荷から解放され、設定も単純になります。
“`nginx
server {
listen 443 ssl; # HTTPSポートでリッスン
server_name yourdomain.com;
ssl_certificate /etc/nginx/certs/yourdomain.crt; # サーバー証明書
ssl_certificate_key /etc/nginx/certs/yourdomain.key; # 秘密鍵
# その他のSSL/TLS設定 (ssl_protocols, ssl_ciphers, etc.)
location / {
proxy_pass http://backend_server; # バックエンドへはHTTPでプロキシ
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; # クライアントからのプロトコル(https)を伝える
}
}
オプション: HTTPでアクセスされた場合にHTTPSにリダイレクト
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
“`
この設定では、Nginxが443番ポートでHTTPS接続を受け付け、SSL/TLSのハンドシェイクと暗号化/復号化を行います。その後、リクエストをバックエンドサーバーの80番ポート(HTTP)に転送します。バックエンドはオリジナルのリクエストがHTTPSだったことをX-Forwarded-Proto: https
ヘッダーで知ることができます。
9.2 Nginx HTTP -> Backend HTTPS
Nginxからバックエンドへの通信をHTTPSで行う場合です。バックエンドが外部に公開されており、Nginxがプロキシとしてその手前に立っている場合などに使用することがあります。
“`nginx
server {
listen 80;
location /secure_backend/ {
proxy_pass https://secure_backend_server; # バックエンドへはHTTPSでプロキシ
# その他のproxy_pass設定...
# バックエンド証明書の検証設定 (本番環境では基本ON)
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; # バックエンド証明書を発行したCA証明書
# クライアント証明書をバックエンドに転送する場合 (バックエンドがクライアント認証を行う場合)
# proxy_ssl_certificate /path/to/nginx_client.crt;
# proxy_ssl_certificate_key /path/to/nginx_client.key;
}
}
“`
proxy_pass
でhttps://...
と指定するだけで、Nginxはバックエンドに対してHTTPSで接続を試みます。
proxy_ssl_verify on;
: バックエンドサーバーが提示する証明書が有効で、信頼できる認証局(CA)によって署名されているかを検証するかどうかを指定します。本番環境ではセキュリティのためon
にすることを強く推奨します。proxy_ssl_trusted_certificate /path/to/ca.crt;
: バックエンド証明書を検証する際に使用する、信頼できるCA証明書バンドルファイルを指定します。システムのCA証明書(例:/etc/ssl/certs/ca-certificates.crt
や/etc/pki/tls/certs/ca-bundle.crt
)を指定することが多いです。proxy_ssl_verify_depth number;
: 信頼チェーンの検証の深さを指定します。proxy_ssl_ciphers string;
/proxy_ssl_protocols string;
: バックエンドとのSSL/TLS通信で使用する暗号スイートやプロトコルバージョンを指定します。proxy_ssl_session_reuse on;
: バックエンドとのSSL/TLSセッションを再利用するかどうか。パフォーマンス向上のため通常on
です。
注意: proxy_ssl_verify off;
と設定すると、バックエンド証明書の検証を行わなくなります。これは、自己署名証明書を使用している内部ネットワーク内など、完全に信頼できる環境以外では非常に危険なので避けるべきです。
9.3 Nginx HTTPS -> Backend HTTPS
クライアントとの通信もNginxとバックエンド間の通信もHTTPSで行う場合です。これはセキュリティ要件が非常に高い場合や、バックエンドサーバーがSSL/TLS終端機能を持っている場合に利用されます。
この場合、Nginxの設定は9.1(クライアント側SSL)と9.2(バックエンド側SSL)の設定を組み合わせる形になります。
“`nginx
server {
listen 443 ssl; # クライアントとのHTTPS
server_name yourdomain.com;
ssl_certificate /etc/nginx/certs/yourdomain.crt;
ssl_certificate_key /etc/nginx/certs/yourdomain.key;
# クライアント側SSL設定...
location / {
proxy_pass https://secure_backend_server; # バックエンドとのHTTPS
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; # クライアントからのプロトコル(https)を伝える
# バックエンド証明書の検証設定 (proxy_ssl_verify, proxy_ssl_trusted_certificate など)
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
# その他のバックエンド側SSL設定...
}
}
“`
10. Upstreamモジュールとロードバランシング
proxy_pass
のターゲットとして、単一のサーバーアドレスではなく、複数のサーバーをグループ化した「アップストリーム」を指定することができます。これにより、複数のバックエンドサーバー間でリクエストを分散するロードバランシングが可能になります。
10.1 upstream
ブロックの定義
upstream
ブロックはhttp
ブロックのトップレベルで定義します。ブロック内で、グループに含めるバックエンドサーバーのリストを指定します。
“`nginx
http {
# … 他の設定 …
upstream my_backend_servers {
server backend1.local:8080; # サーバーアドレスまたはホスト名
server 192.168.1.101;
server unix:/var/run/backend3.sock; # Unixドメインソケットも可能
# 他のサーバー...
}
server {
listen 80;
location /app/ {
proxy_pass http://my_backend_servers; # ここでupstream名を指定
# その他のproxy_ ディレクティブ...
}
}
}
“`
10.2 ロードバランシングメソッド
upstream
ブロックは、デフォルトで「ラウンドロビン (Round Robin)」という方式でリクエストをサーバーに順番に分散します。しかし、他のロードバランシングアルゴリズムを指定することも可能です。
- ラウンドロビン (デフォルト): リクエストを各サーバーに順番に振り分けます。設定なしで利用できます。
least_conn
: アクティブな接続数が最も少ないサーバーにリクエストを振り分けます。長時間接続が必要なアプリケーションに適しています。
nginx
upstream my_backend_servers {
least_conn;
server backend1.local;
server backend2.local;
}ip_hash
: クライアントのIPアドレスのハッシュ値に基づいてサーバーを決定します。同じクライアントからのリクエストは常に同じサーバーに振り分けられます。セッションの維持が必要なアプリケーション(ステートフルなアプリケーション)に適していますが、特定のIPからのアクセスが多いと負荷が偏る可能性があります。
nginx
upstream my_backend_servers {
ip_hash;
server backend1.local;
server backend2.local;
}hash key [consistent];
: 指定したキー(変数や組み合わせ)のハッシュ値に基づいてサーバーを決定します。consistent
パラメータを付けると、アップストリームサーバーの増減があった場合のキャッシュミス(振り分け先の変更)を最小限に抑えられます。
nginx
upstream my_backend_servers {
hash $request_uri consistent; # リクエストURIに基づいて振り分け
server backend1.local;
server backend2.local;
}random [method];
: ランダムにサーバーを選択します。least_conn
やضهights
などのメソッドと組み合わせることもできます。
nginx
upstream my_backend_servers {
random two least_conn; # 2つのサーバーからleast_connで選択
server backend1.local;
server backend2.local;
server backend3.local;
}
10.3 サーバーパラメータ
upstream
ブロック内の各server
行には、ロードバランシングの挙動を制御するためのパラメータを指定できます。
weight=number
: サーバーへのリクエストの割り当てにおける重みを指定します(デフォルトは1)。重みが高いほど、より多くのリクエストが割り当てられます。
nginx
upstream my_backend_servers {
server backend1.local weight=3; # 3倍のリクエストを受け取る
server backend2.local weight=1;
}max_fails=number
: 指定された時間枠(fail_timeout
)内に、サーバーへのリクエストが指定された回数(デフォルトは1)失敗した場合、そのサーバーはダウンしていると見なされ、指定された時間(fail_timeout
)だけトラフィックの振り分けから除外されます。fail_timeout=time
:max_fails
の失敗回数をカウントする時間枠、およびサーバーがダウンしていると見なされた場合にトラフィックから除外される時間(デフォルトは10秒)です。
nginx
upstream my_backend_servers {
server backend1.local max_fails=3 fail_timeout=30s; # 30秒以内に3回失敗したら、次の30秒間除外
server backend2.local;
}down
: そのサーバーを永続的にトラフィックから除外します。メンテナンス時などに使用します。
nginx
upstream my_backend_servers {
server backend1.local down; # このサーバーには振り分けられない
server backend2.local;
}backup
: バックアップサーバーとして指定します。プライマリサーバーが全てダウンした場合にのみトラフィックが振り分けられます。
nginx
upstream my_backend_servers {
server backend1.local;
server backend2.local;
server backend3.local backup; # backup1とbackup2が全てダウンした場合に利用
}
これらのパラメータを組み合わせることで、バックエンドサーバーの状態に応じた柔軟なロードバランシングとヘルスチェック(後述のパッシブヘルスチェック)を設定できます。
10.4 Keepalive Connections
Nginxとバックエンドサーバー間でコネクションを再利用する(Keepalive接続)ことで、コネクション確立のオーバーヘッドを減らし、パフォーマンスを向上させることができます。
これはupstream
ブロック内でkeepalive
ディレクティブを使用して設定します。
nginx
upstream my_backend_servers {
server backend1.local;
server backend2.local;
keepalive 64; # Nginxワーカープロセスごとに、アップストリームサーバーへのアイドル状態のkeepalive接続を最大64個保持
}
keepalive number;
は、Nginxの各ワーカープロセスがアップストリームサーバーへのアイドル状態のkeepalive接続を最大いくつ保持するかを指定します。これにより、新しいリクエストが来た際に、既存のアイドル状態のコネクションを再利用できるようになります。ただし、バックエンドサーバー側でもkeepalive接続を受け付けるように設定されている必要があります。
この設定は、バックエンドへの接続数が多い場合に特に有効です。
11. ヘルスチェック
ロードバランシングにおいて、バックエンドサーバーが正常に稼働しているかを確認するヘルスチェックは非常に重要です。Nginxには、基本的なパッシブヘルスチェック機能が内蔵されています。より高度なアクティブヘルスチェックは、通常Nginx Plus(商用版)で提供されますが、オープンソース版でもサードパーティモジュールで実現できます。
11.1 パッシブヘルスチェック (内蔵機能)
これは、実際のリクエストの成否に基づいてサーバーの状態を判断する仕組みです。前述のmax_fails
とfail_timeout
パラメータを使用して設定します。
Nginxはバックエンドサーバーにリクエストを転送し、以下のいずれかの条件が満たされた場合に「失敗」とカウントします。
- コネクション確立に失敗した。
- リクエスト送信に失敗した。
- 応答ヘッダーを受信する前にタイムアウトした(
proxy_read_timeout
)。 - 無効な応答を受信した。
指定されたfail_timeout
時間内にmax_fails
回以上失敗した場合、そのサーバーはfail_timeout
時間だけダウンしていると見なされ、トラフィックの振り分けから一時的に除外されます。fail_timeout
時間が経過すると、Nginxはそのサーバーに次のリクエストを「試行」として送り、成功すれば再度稼働状態に戻します。
設定例:
nginx
upstream my_backend_servers {
server backend1.local:8080 max_fails=3 fail_timeout=15s; # 15秒間に3回失敗したら、次の15秒間は除外
server backend2.local:8080;
server backend3.local:8080 backup; # バックアップサーバーはデフォルトでmax_fails=1
}
これは設定が容易で、バックエンドの一般的な障害に対応できますが、特定のURLへのアクセスは成功するがアプリケーションの特定の機能が壊れている、といったより複雑なアプリケーションレベルの問題は検出できません。
11.2 アクティブヘルスチェック (Nginx Plusまたはサードパーティモジュール)
Nginx Plusは、バックエンドサーバーに定期的に専用のヘルスチェックリクエスト(例: 特定のURLへのHTTP GETリクエスト)を送信し、その応答(ステータスコード、応答ボディの内容など)に基づいてサーバーの状態を判断する「アクティブヘルスチェック」機能を提供します。
オープンソース版のNginxでは、nginx_upstream_check_module
のようなサードパーティ製のモジュールを組み込むことで同様の機能を実現できる場合があります。
アクティブヘルスチェックは、実際のリクエストに依存せず、より能動的かつ柔軟にバックエンドの状態を監視できるため、より信頼性の高いシステムを構築する上で有効です。
12. よくある設定ミスとトラブルシューティング
proxy_pass
の設定は非常に強力ですが、URI変換ルールやヘッダー処理などを間違えると、意図しない挙動になったりエラーが発生したりします。ここでは、よくある設定ミスと、問題発生時のトラブルシューティング方法を解説します。
12.1 よくあるエラーコードと原因
-
502 Bad Gateway:
- 原因: Nginxがバックエンドサーバーから無効な応答を受け取った。
- 主な状況:
- バックエンドサーバーがダウンしているか、応答していない。
- バックエンドサーバーが過負荷になっている。
- バックエンドサーバーが予期せぬ応答(プロトコル違反など)を返した。
- Nginxからバックエンドへの接続が確立できない(
proxy_connect_timeout
でタイムアウトすることも)。
- 対処法:
- バックエンドサーバーが起動しているか、正常に動作しているか確認する。
- Nginxからバックエンドサーバーへのネットワーク接続を確認する(
curl
やtelnet
コマンドなどで)。 - バックエンドサーバーのログを確認し、エラーが出ていないか確認する。
- Nginxの
error_log
を確認する。接続拒否やコネクションリセットなどのエラーメッセージが出ている場合がある。
-
504 Gateway Timeout:
- 原因: Nginxがバックエンドサーバーからの応答を指定された時間(
proxy_read_timeout
)内に受信できなかった。 - 主な状況:
- バックエンドサーバーでのリクエスト処理に時間がかかりすぎている。
- バックエンドサーバーがデッドロックしているか、ハングアップしている。
- ネットワーク遅延や帯域幅の問題で、応答データの転送が遅れている。
- Nginxの
proxy_read_timeout
設定が短すぎる。
- 対処法:
- バックエンドサーバーの処理時間を確認する。必要であればバックエンド側のパフォーマンスを改善する。
- Nginxの
proxy_read_timeout
設定を、バックエンドの最大処理時間より少し長めに調整する。 - Nginxからバックエンドへのネットワークの状態を確認する。
- バックエンドサーバーのログを確認し、タイムアウトの原因となる処理遅延が発生していないか確認する。
- 原因: Nginxがバックエンドサーバーからの応答を指定された時間(
-
404 Not Found (意図せず):
- 原因: Nginxがバックエンドに転送したURIが、バックエンド側で存在しないリソースを指している。
- 主な状況:
- URI変換ルールの誤解: 前述の末尾スラッシュやパスの扱いの違いにより、Nginxがバックエンドに間違ったURIを転送している可能性が最も高い。
location
ブロックのマッチング設定が間違っている。- バックエンドサーバーで、そのURIに対応するリソースが本当に存在しない。
- 対処法:
- 最も重要: セクション3で解説したURI変換ルールを再確認し、
location
ブロックとproxy_pass
のパス設定が意図通りになっているか確認する。特に末尾のスラッシュの有無を注意深くチェックする。 - Nginxのアクセスログ(
access_log
)で、Nginxがバックエンドに転送している実際のリクエストURIを確認する(後述)。 - バックエンドサーバーのアクセスログを確認し、どのようなURIのリクエストを受け取っているか確認する。
rewrite
ディレクティブを使用している場合は、その設定が正しくURIを書き換えているか確認する。
- 最も重要: セクション3で解説したURI変換ルールを再確認し、
12.2 トラブルシューティングの手順
-
ログの確認:
- Nginxのエラーログ (
error_log
): Nginx自身で発生したエラー(接続失敗、タイムアウト、設定エラーなど)が記録されます。エラーメッセージとタイムスタンプを確認し、問題発生時の状況を把握します。 - Nginxのアクセスログ (
access_log
): クライアントからのリクエストと、Nginxがそれをどのように処理したか(ステータスコード、転送時間など)が記録されます。カスタムログフォーマットを使用して、Nginxがバックエンドに転送したURIやヘッダー(例:$upstream_uri
,$http_host
,$proxy_protocol_server_addr
など)を記録するように設定すると、デバッグに役立ちます。 - バックエンドサーバーのログ: バックエンドアプリケーションやWebサーバー(Tomcat, Gunicorn, PHP-FPMなど)のログを確認し、リクエストがバックエンドに到達しているか、バックエンド側でエラーが発生していないか確認します。
- Nginxのエラーログ (
-
ネットワーク接続の確認:
- NginxサーバーからバックエンドサーバーのIPアドレスとポートに対して、
telnet
やnc
(netcat)コマンドで接続できるか確認します。例:telnet backend.local 8080
curl
コマンドで、Nginxサーバーから直接バックエンドサーバーにリクエストを送信し、応答があるか確認します。例:curl -v http://backend.local:8080/testpath
これはNginxを通さない直接アクセスなので、バックエンド自体の問題を切り分けるのに役立ちます。
- NginxサーバーからバックエンドサーバーのIPアドレスとポートに対して、
-
Nginx設定の確認とリロード:
nginx -t
コマンドで設定ファイルの構文エラーがないか確認します。- 設定を変更した後は、必ず
nginx -s reload
またはnginx -s reopen
(ログファイルをローテーションする場合) で設定を反映させます。nginx -s stop
/nginx -s start
はサーバーを完全に停止/起動するので、サービス中断を伴います。 - 特にURI変換に関する問題では、セクション3のルールを紙やエディタで追いながら、意図した変換がされる設定になっているか再度確認します。
-
段階的なデバッグ:
proxy_pass
のターゲットを、一時的にNginx自身が配信する静的なファイルなど、確実に動作するものに変更してみる。これにより、問題がNginxの設定にあるのか、バックエンドにあるのかを切り分けられます。- 最小限の設定で動作確認し、徐々に設定を追加していく。
proxy_buffering off;
を試してみる(ただし、これはキャッシュやGzipに影響します)。
デバッグは、問題の切り分けと原因特定のプロセスです。ログとネットワークツールを駆使し、Nginxとバックエンドのどちらに問題があるのか、そしてNginxの設定のどの部分が予期しない挙動を引き起こしているのかを特定することが重要です。
12.3 アクセスログでのURI確認
Nginxのアクセスログに転送先のURIを記録させることで、URI変換のデバッグが容易になります。http
ブロックでlog_format
を定義し、server
ブロックやlocation
ブロックでそれを指定します。
“`nginx
http {
log_format proxy_debug ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘$status $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘
‘ proxy_pass_uri=”$upstream_uri” upstream_addr=”$upstream_addr”‘;
server {
listen 80;
access_log logs/access_proxy_debug.log proxy_debug; # このフォーマットを使用
location /app/ {
proxy_pass http://backend/; # 例: 末尾スラッシュあり
# proxy_pass http://backend; # 例: 末尾スラッシュなし
proxy_set_header Host $host;
# ...
}
}
}
“`
上記の例で、proxy_debug
フォーマットにproxy_pass_uri="$upstream_uri"
を追加しました。$upstream_uri
変数には、Nginxがバックエンドに転送しようとしたリクエストラインのURI部分(クエリ文字列は含まない)が記録されます。
location /app/ { proxy_pass http://backend/; }
の場合、/app/resource
へのリクエストに対するログには proxy_pass_uri="/resource"
のように記録されるはずです。
location /app/ { proxy_pass http://backend; }
の場合、/app/resource
へのリクエストに対するログには proxy_pass_uri="/app/resource"
のように記録されるはずです。
このログを見ることで、NginxがバックエンドにどのようなURIでリクエストを投げているかを正確に把握し、URI変換の誤りを特定できます。
13. 応用的な設定
基本的なproxy_pass
機能に加えて、さらに柔軟性や制御を強化するための応用的なディレクティブがいくつかあります。
-
proxy_redirect
: バックエンドサーバーからの応答に含まれるLocation
ヘッダー(リダイレクトURL)やRefresh
ヘッダーに含まれるURLを、クライアントに返す前にNginxが書き換える必要がある場合に用います。例えば、バックエンドが内部アドレス(例:http://backend/login
)でリダイレクト応答を返した場合、それをクライアントがアクセスできるアドレス(例:http://yourdomain.com/app/login
)に書き換えるために使用します。
nginx
location /app/ {
proxy_pass http://backend/;
# バックエンドが返す "/login" というパスを "/app/login" に書き換える
# proxy_redirect default; # proxy_passのURIに基づいて自動的に書き換える (多くのケースで十分)
proxy_redirect http://backend/ http://yourdomain.com/app/; # 明示的に指定
}
proxy_redirect default;
は、proxy_pass
のURIとlocation
ブロックのURIに基づいて自動的に書き換えルールを推測してくれるため、非常に便利です。 -
proxy_set_body
: バックエンドに送信するリクエストボディを変更する場合に使用します。例えば、元のリクエストボディを削除したり、固定の内容に置き換えたりする場合などです。
nginx
location /api/old/ {
proxy_pass http://backend;
proxy_set_body '{"message": "Request received"}'; # リクエストボディを固定文字列に置き換え
proxy_set_header Content-Type 'application/json';
} -
proxy_method
: バックエンドに送信するリクエストのHTTPメソッドを変更する場合に使用します。例えば、クライアントからのPOSTリクエストをバックエンドにはPUTとして送信したい場合などです。
nginx
location /update/ {
proxy_pass http://backend;
proxy_method PUT; # クライアントからのメソッドに関わらずPUTとして送信
} -
proxy_ignore_headers
: バックエンドからの応答に含まれる特定のヘッダーをNginxが無視するように指定します。例えば、バックエンドが設定するキャッシュ関連のヘッダー(Cache-Control
,Expires
,Set-Cookie
など)を無視して、Nginxのキャッシュ設定やヘッダー設定を優先させたい場合に用います。
nginx
location /api/ {
proxy_pass http://backend;
proxy_cache my_cache;
proxy_cache_valid 200 10m;
proxy_ignore_headers Cache-Control Expires Set-Cookie; # バックエンドのこれらのヘッダーを無視
}
これらの応用的な設定は、特定のユースケースやバックエンドサーバーの仕様に合わせて、より詳細な制御を行うために利用されます。
14. まとめと次のステップ
この記事では、Nginx proxy_pass
ディレクティブについて、その基本的な概念であるリバースプロキシの役割から始まり、以下の内容を詳細に解説しました。
proxy_pass
の基本的な設定とlocation
ブロックとの連携。proxy_pass
におけるURI変換ルール、特に末尾スラッシュの重要性と具体的な挙動の違い。proxy_set_header
を使ったリクエストヘッダーの制御方法(Host
,X-Forwarded-For
,X-Forwarded-Proto
など)。proxy_buffering
によるレスポンスバッファリングの仕組みと利点/欠点。proxy_cache
を使ったキャッシュ設定によるパフォーマンス向上策。proxy_connect_timeout
,proxy_send_timeout
,proxy_read_timeout
によるタイムアウト設定。proxy_intercept_errors
を使ったエラーハンドリングとカスタムエラーページの表示。- SSL/TLSを使ったセキュアなプロキシ設定。
upstream
ブロックと各種ロードバランシングメソッド、サーバーパラメータを使った負荷分散。- パッシブヘルスチェックの仕組み。
- よくある設定ミス(特にURI変換)と、ログを使ったトラブルシューティング方法。
proxy_redirect
などの応用的な設定。
proxy_pass
は非常に多機能で、Webアプリケーションのデプロイにおいて中心的な役割を担います。この記事で解説した内容は、Nginxをリバースプロキシとして活用するための基礎となります。
次のステップとして:
- 実際に手を動かす: 解説した設定例を参考に、Nginxをインストールした環境で実際に
proxy_pass
を設定し、簡単なバックエンドアプリケーション(例えばPythonのFlaskやNode.jsのExpressなどで「Hello, World!」を返すだけのもの)と組み合わせて動作確認してみましょう。 - ログを観察する:
access_log
やerror_log
を注意深く観察し、リクエストがどのように処理され、バックエンドにどのように転送されているかを確認しましょう。カスタムログフォーマットで$upstream_uri
などを記録するのも有効です。 - 公式ドキュメントを参照する: Nginxの公式ドキュメント(特に
ngx_http_proxy_module
のページ)は、全てのディレクティブの網羅的な情報源です。この記事で触れられなかったさらに詳細なオプションや、特定のシナリオでの挙動を確認する際に役立ちます。 - より複雑なケースに挑戦する: ロードバランシング、キャッシュ、SSL終端など、複数の機能を組み合わせた設定に挑戦してみましょう。
Nginxとproxy_pass
をマスターすることは、現代のWebサービスを構築・運用する上で非常に強力な武器となります。この記事が、その学びの「入門」として、そして今後のNginx活用における参考資料として、皆様のお役に立てれば幸いです。