Nginx proxy_pass の末尾のスラッシュ:404エラーとパスのマッピングエラーの解決

intermediate Nginx2026-07-26| Linux (Ubuntu, CentOS, Debian), Nginx 1.10+, Docker コンテナ, マイクロサービスアーキテクチャ

Error Message

404 Not Found hoặc request bị chuyển hướng sai path khi dùng proxy_pass tới backend
#nginx#proxy_pass#末尾のスラッシュ#リバースプロキシ#devops

午前2時のデプロイの悪夢新しいマイクロサービスを本番環境にプッシュしたばかりだとしましょう。コンテナ内部ではすべてが完璧に見えます。curl localhost:8080/api/health は 200 OK を返します。しかし、Nginx ゲートウェイ経由でアクセスしようとした瞬間、404 Not Found が発生します。さらに奇妙なことに、バックエンドのログには //user/profile/api/api/user/profile といった、パスが崩れたリクエストが記録されています。Nginx は動作しており、アップストリームも稼働していますが、ルーティングが壊れています。

エラーメッセージログや自動監視ツールでは通常、以下のように報告されます:

404 Not Found hoặc request bị chuyển hướng sai path khi dùng proxy_pass tới backend

ロジック:なぜスラッシュ1つですべてが変わるのかNginx は、URL に URI(パス、あるいは単なる末尾のスラッシュ)が含まれているかという些細な詳細に基づいて、proxy_pass ディレクティブを異なる方法で処理します。この動作は、リバースプロキシ設定エラーの大部分を占めています。

シナリオ 1:proxy_pass に URI がない場合(「パススルー」)ホスト名やポートの後の末尾のスラッシュを省略すると、Nginx は受信した元のリクエスト URI をそのまま渡します。文字列の変更は行われません。

location /api/ {
    proxy_pass http://backend_server;
}
```- **受信リクエスト:** `/api/users`- **バックエンドに送信されるパス:** `/api/users`- **推奨されるケース:** フルプレフィックスを必要とするモノリスやバックエンド。### シナリオ 2:proxy_pass に URI がある場合(「置換」)`proxy_pass` に末尾のスラッシュ (`/`) を追加すると、Nginx はそれを置換として扱います。location ブロックに**一致しなかった** URI の部分を計算し、それを `proxy_pass` で定義されたパスに追加します。

location /api/ { proxy_pass http://backend_server/; } ```- 受信リクエスト: /api/users- バックエンドに送信されるパス: /users- 注意: /api/ の部分は取り除かれ、proxy_pass で定義された / に置き換えられました。## 設定の修正方法### 1. フルパスを維持するバックエンドサービス(Spring Boot や Express アプリなど)が /api/... でリッスンするように設定されている場合は、末尾のスラッシュを使用しないでください。これは、ほとんどのマイクロサービスにとって最も安全なデフォルト設定です。

location /app/ {
    proxy_pass http://127.0.0.1:8080;
}

/app/login へのリクエストは、バックエンドに /app/login として到達します。

2. プレフィックスを正しく削除するPrometheus のダッシュボードや特定の API など、多くのスタンドアロンサービスはルート (/) で動作することを想定しています。/service-a/ を、自分がプロキシの背後にいることを知らないバックエンドにマッピングする場合は、location と proxy_pass の両方に末尾のスラッシュを使用する必要があります。

location /service-a/ {
    proxy_pass http://service_a_upstream/;
}

二重スラッシュに注意: スラッシュなしの location /service-a と、スラッシュありの proxy_pass http://host/ を組み合わせて使用すると、/service-a/login へのリクエストは //login になります。多くの厳格なフレームワークは、これを 404 または 400 Bad Request として拒否します。

3. 正規表現による代替案ルーティングロジックが複雑になった場合は、rewrite を使用してください。これは、スラッシュのルールを覚えるよりも読みやすく、パスの「オフバイワン(1つずれ)」エラーを防ぐことができます。

location /api/ {
    rewrite ^/api/(.*)$ /$1 break;
    proxy_pass http://backend_server;
}

検証手順推測をやめて、測定を始めましょう。これらのツールを使用して、Nginx が実際に何を行っているかを確認してください。

アップストリームのログを確認するnginx.conf でカスタムログ形式を定義します。これにより、Nginx がバックエンドサーバーに送信している正確なパスが明らかになります。

log_format debug_proxy '$remote_addr - $request - to: $upstream_addr ' 
                       'uri: $uri path_sent: $request_uri';
access_log /var/log/nginx/access.log debug_proxy;

Curl で検査するcurl -Iv を使用して、301 リダイレクトを確認します。Nginx は /api/api/ に自動的にリダイレクトすることが多く、これがフロントエンドクライアントや API コンシューマーを混乱させることがあります。

curl -Iv http://your-domain.com/api/user

まとめチェックリスト- 一貫性が重要: location がスラッシュで終わる場合、通常は proxy_pass もスラッシュで終わる必要があります。- フレームワークの感度: Django や Flask は、/path/path/ を異なるリソースとして扱う場合があることに注意してください。- エッジケースのテスト: リダイレクトロジックがユーザーエクスペリエンスを損なわないよう、常に末尾のスラッシュがない URL(例:/api/api/)をテストしてください。

Related Error Notes