運用中の商用環境において、バックエンドサービスは正常に稼働しているにもかかわらず、フロントエンドからのリクエストが一部404(Not Found)エラーになるという問題が発生しました。本記事では、この問題を特定し、Nginxのデバッグモードを活用して解決に至ったプロセスを技術的に解説します。
問題の発生と初期診断
まず、バックエンドのサービスプロセスとポートが正常にリッスンされているかを確認します。直接バックエンドのIPとポートに対してcurlコマンドでリクエストを送信したところ、正常なレスポンスが返ってきました。しかし、Nginxを経由すると以下のアクセスログが示す通り、404エラーが発生していました。
# Nginx access.log
10.0.0.50 - - [2024-05-15:10:00:01 +0900] "GET /api/v1/user/profile/get_info HTTP/1.1" 404 0 "https://example.com/dashboard/" "Mozilla/5.0..."
この結果から、問題はバックエンドではなく、Nginxのリクエストルーティング層にあることが判明しました。
Nginxデバッグモジュールの有効化
詳細なマッチングロジックを追跡するため、Nginxのデバッグログを有効にします。多くの環境ではデフォルトでデバッグモジュールが含まれていないため、現在のコンパイルオプションを確認します。
nginx -V
--with-debugが含まれていない場合は、ソースコードから再ビルドする必要があります(既存のバイナリを上書きしないよう、バックアップを推奨します)。
# Nginxの再ビルド手順
./configure --prefix=/etc/nginx --with-http_ssl_module --with-debug
make
# インストールはせず、バイナリのみ差し替える
cp objs/nginx /usr/local/sbin/nginx_debug
設定ファイルでログレベルをdebugに変更します。
error_log /var/log/nginx/error.log debug;
ログ解析と原因特定
デバッグログを有効にした状態でリクエストを送信し、error.logを確認すると、NginxがどのLocationブロックを選択しているかが表示されます。
[debug] 1234#0: *1 http uri: "/api/v1/user/profile/get_info"
[debug] 1234#0: *1 test location: "/"
[debug] 1234#0: *1 test location: "static/"
[debug] 1234#0: *1 test location: ~ "/user"
[debug] 1234#0: *1 using configuration "/user"
ここで問題が明らかになりました。本来/api/v1で始まるLocationにマッチすべきリクエストが、URL内に含まれる/userという文字列に反応し、別のLocationブロックに引き込まれていました。当時の設定は以下の通りです。
location ~/user {
proxy_pass http://user_service;
}
location ~/api {
proxy_pass http://api_gateway;
}
Nginxの正規表現マッチング(~)は、記述順序や部分一致の性質により、意図しないLocationが優先されることがあります。今回、/api/v1/user/...というURLが、先に定義されていた~/userにマッチしてしまい、存在しないパスとして404を返していました。
解決策:マッチング精度の向上
不要な正規表現マッチングを避け、プレフィックス一致(前方一致)を利用するように設定を修正しました。
# 修正後の設定
location /api/v1/user/ {
proxy_pass http://user_backend;
}
location /api/v1/ {
proxy_pass http://main_gateway;
}
~を取り除くことで、Nginxは最長一致(Longest Prefix Match)のルールに従い、より正確なLocationを選択するようになります。修正後、設定をリロードして問題が解決したことを確認しました。
Nginx Locationマッチング規則のまとめ
1. 基本構文
location [修飾子] /uri/ { ... }
=: 完全一致。マッチした時点で検索を終了します。^~: 前方一致。正規表現の検査をスキップします。~: 正規表現(大文字小文字を区別)。~*: 正規表現(大文字小文字を区別しない)。- (なし) : 前方一致。
2. root と alias の違い
静的ファイルを配信する際、パスの解決方法が異なります。
# rootの場合: /media/img/test.jpg を探す
location /media/ {
root /data/;
}
# aliasの場合: /data/img/test.jpg を探す
location /media/ {
alias /data/;
}
3. rewriteにおけるlastとbreak
last: 書き換え後、再度Locationのマッチングを最初からやり直します。break: 書き換え後、現在のLocationブロック内で処理を完結させます。
商用環境での404エラーは、単純なパスミスだけでなく、このようなNginxの評価順序による競合が原因となるケースが多くあります。デバッグログを活用し、リクエストがどのLocationにトラップされているかを正確に把握することが解決の近道です。