【入門】Nginx proxy_passの徹底解説と設定方法

はい、承知いたしました。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ブロックとの連携、特に末尾のスラッシュの重要性を理解し、意図したパスへの転送を実現する。
  • リクエストヘッダー(HostX-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つの形式があります。

  1. URLとして指定: http://host[:port] または https://host[:port] または http://unix:/path/to/socket の形式で、プロトコル、ホスト名(またはIPアドレス)、オプションでポート番号を指定します。この形式で指定する場合、末尾にパスを含めることもできます(例: http://backend/some/path/)。
  2. 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
  • 例 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 が直接結合されてしまいます。これは意図しない挙動となることが多いです。
  • 例 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/以下のパス構造をそのままバックエンドのルートパス以下にマッピングしたい場合に適しています。
  • 例 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/ がそのままバックエンドに渡されます。
  • 例 5: location /proxy_pass http://backend5/;

    • リクエストURI: /any/path/
    • location / にマッチ。マッチした部分 / は取り除かれる(ルートパスのみ)。残りは any/path/
    • proxy_pass のURLにパス部分 / が指定されている。残りの部分 any/path/ が、この /末尾に付加される。
    • 転送先URI: http://backend5/any/path/
    • 全てのトラフィックをバックエンドのルート以下にマッピングする標準パターン。
  • 例 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全体と同じになるためです。
  • 例 7: location = /exactproxy_pass http://backend7;

    • リクエストURI: /exact
    • location = /exact に完全に一致。proxy_pass のURLにパス部分が指定されていない。
    • オリジナルのURI /exact 全体が、転送先のURLに付加される。
    • 転送先URI: http://backend7/exact
  • 例 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ディレクティブと組み合わせる必要があります。
  • 例 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

これらの例からわかる最も重要なポイントは、location /prefix/ { proxy_pass http://backend; } のように、locationに末尾スラッシュがあるのにproxy_passにパスがない形式は、意図しないURI(オリジナルのパス全体が付加される)になる可能性が高いため、避けるべきであるということです。

推奨される安全なパターンは以下の2つです。

  1. location /prefix/ { proxy_pass http://backend/; }: /prefix/以下をバックエンドのルート(/)以下にマッピングする場合。
  2. location /prefix { proxy_pass http://backend/newpath; }: /prefixをバックエンドの /newpath にマッピングし、/prefix/suffix/newpathsuffix にマッピングする場合。(ただし、proxy_passのパスに末尾スラッシュがない場合、パス結合に注意が必要)
  3. 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 キャッシュの仕組み

  1. クライアントからNginxにリクエストが来る。
  2. Nginxは指定されたキャッシュキー(通常はリクエストURIなどに基づいて生成される)に対応するキャッシュエントリを検索する。
  3. キャッシュヒット: キャッシュエントリが見つかり、有効期限内であれば、Nginxはそのキャッシュされた応答をクライアントに返す。バックエンドにはアクセスしない。
  4. キャッシュミス: キャッシュエントリが見つからない、または有効期限切れの場合、Nginxはリクエストをバックエンドサーバーに転送する。
  5. バックエンドからの応答を受信したら、Nginxはその応答を指定された期間キャッシュに保存する。
  6. 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:2c/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-SinceIf-None-Matchヘッダーをバックエンドに送信し、コンテンツが変更されていなければ304 Not Modified応答を受け取ってキャッシュを更新(有効期限を延長)します。

キャッシュは適切に設定すれば大きな効果を発揮しますが、キャッシュすべきでない応答(ユーザー固有の情報を含むページなど)をキャッシュしたり、キャッシュの有効期限を間違えたりすると、古い情報が表示されてしまう原因にもなります。キャッシュ戦略はアプリケーションの特性に合わせて慎重に設計する必要があります。

7. タイムアウト設定 (proxy_connect_timeout, proxy_send_timeout, proxy_read_timeout)

リバースプロキシでは、Nginxとバックエンドサーバー間の通信におけるタイムアウト設定が非常に重要です。これらの設定が適切でないと、バックエンドの応答が遅い場合にクライアントにエラーを返してしまったり、Nginxのリソースが長時間占有されてしまったりします。

Nginxのproxy_passに関連する主なタイムアウト設定は以下の3つです。

  1. proxy_connect_timeout time;: NginxがバックエンドサーバーとのTCPコネクションを確立しようとする際の最大時間。この時間内にコネクションが確立できない場合、Nginxは通常502 Bad Gatewayエラーを返します。

    • デフォルト: 60s
    • 設定例: proxy_connect_timeout 5s; (短く設定することで、応答しないサーバーへの接続試行を早期に打ち切る)
  2. proxy_send_timeout time;: Nginxがバックエンドサーバーにリクエストを送信する際、write操作の間に応答を待つ最大時間。バックエンドが指定された時間内にデータを受け付けない場合、Nginxはリクエスト送信を中止します。

    • デフォルト: 60s
    • 設定例: proxy_send_timeout 10s;
  3. 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_passhttps://...と指定するだけで、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_failsfail_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からバックエンドサーバーへのネットワーク接続を確認する(curltelnetコマンドなどで)。
      • バックエンドサーバーのログを確認し、エラーが出ていないか確認する。
      • Nginxのerror_logを確認する。接続拒否やコネクションリセットなどのエラーメッセージが出ている場合がある。
  • 504 Gateway Timeout:

    • 原因: Nginxがバックエンドサーバーからの応答を指定された時間(proxy_read_timeout)内に受信できなかった。
    • 主な状況:
      • バックエンドサーバーでのリクエスト処理に時間がかかりすぎている。
      • バックエンドサーバーがデッドロックしているか、ハングアップしている。
      • ネットワーク遅延や帯域幅の問題で、応答データの転送が遅れている。
      • Nginxのproxy_read_timeout設定が短すぎる。
    • 対処法:
      • バックエンドサーバーの処理時間を確認する。必要であればバックエンド側のパフォーマンスを改善する。
      • Nginxのproxy_read_timeout設定を、バックエンドの最大処理時間より少し長めに調整する。
      • Nginxからバックエンドへのネットワークの状態を確認する。
      • バックエンドサーバーのログを確認し、タイムアウトの原因となる処理遅延が発生していないか確認する。
  • 404 Not Found (意図せず):

    • 原因: Nginxがバックエンドに転送したURIが、バックエンド側で存在しないリソースを指している。
    • 主な状況:
      • URI変換ルールの誤解: 前述の末尾スラッシュやパスの扱いの違いにより、Nginxがバックエンドに間違ったURIを転送している可能性が最も高い。
      • locationブロックのマッチング設定が間違っている。
      • バックエンドサーバーで、そのURIに対応するリソースが本当に存在しない。
    • 対処法:
      • 最も重要: セクション3で解説したURI変換ルールを再確認し、locationブロックとproxy_passのパス設定が意図通りになっているか確認する。特に末尾のスラッシュの有無を注意深くチェックする。
      • Nginxのアクセスログ(access_log)で、Nginxがバックエンドに転送している実際のリクエストURIを確認する(後述)。
      • バックエンドサーバーのアクセスログを確認し、どのようなURIのリクエストを受け取っているか確認する。
      • rewriteディレクティブを使用している場合は、その設定が正しくURIを書き換えているか確認する。

12.2 トラブルシューティングの手順

  1. ログの確認:

    • Nginxのエラーログ (error_log): Nginx自身で発生したエラー(接続失敗、タイムアウト、設定エラーなど)が記録されます。エラーメッセージとタイムスタンプを確認し、問題発生時の状況を把握します。
    • Nginxのアクセスログ (access_log): クライアントからのリクエストと、Nginxがそれをどのように処理したか(ステータスコード、転送時間など)が記録されます。カスタムログフォーマットを使用して、Nginxがバックエンドに転送したURIやヘッダー(例: $upstream_uri, $http_host, $proxy_protocol_server_addrなど)を記録するように設定すると、デバッグに役立ちます。
    • バックエンドサーバーのログ: バックエンドアプリケーションやWebサーバー(Tomcat, Gunicorn, PHP-FPMなど)のログを確認し、リクエストがバックエンドに到達しているか、バックエンド側でエラーが発生していないか確認します。
  2. ネットワーク接続の確認:

    • NginxサーバーからバックエンドサーバーのIPアドレスとポートに対して、telnetnc(netcat)コマンドで接続できるか確認します。例: telnet backend.local 8080
    • curlコマンドで、Nginxサーバーから直接バックエンドサーバーにリクエストを送信し、応答があるか確認します。例: curl -v http://backend.local:8080/testpath これはNginxを通さない直接アクセスなので、バックエンド自体の問題を切り分けるのに役立ちます。
  3. Nginx設定の確認とリロード:

    • nginx -t コマンドで設定ファイルの構文エラーがないか確認します。
    • 設定を変更した後は、必ず nginx -s reload または nginx -s reopen (ログファイルをローテーションする場合) で設定を反映させます。nginx -s stop / nginx -s start はサーバーを完全に停止/起動するので、サービス中断を伴います。
    • 特にURI変換に関する問題では、セクション3のルールを紙やエディタで追いながら、意図した変換がされる設定になっているか再度確認します。
  4. 段階的なデバッグ:

    • 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_logerror_logを注意深く観察し、リクエストがどのように処理され、バックエンドにどのように転送されているかを確認しましょう。カスタムログフォーマットで$upstream_uriなどを記録するのも有効です。
  • 公式ドキュメントを参照する: Nginxの公式ドキュメント(特にngx_http_proxy_moduleのページ)は、全てのディレクティブの網羅的な情報源です。この記事で触れられなかったさらに詳細なオプションや、特定のシナリオでの挙動を確認する際に役立ちます。
  • より複雑なケースに挑戦する: ロードバランシング、キャッシュ、SSL終端など、複数の機能を組み合わせた設定に挑戦してみましょう。

Nginxとproxy_passをマスターすることは、現代のWebサービスを構築・運用する上で非常に強力な武器となります。この記事が、その学びの「入門」として、そして今後のNginx活用における参考資料として、皆様のお役に立てれば幸いです。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール