Nginx の 499 エラー:原因と解決策
冒頭まとめ アクセスログに残る 499 は、Nginx が応答を返す前にクライアント側から接続が切られたことを示すコードです。HTTP の標準にはない Nginx 独自のコードで、相手はすでにいないため、利用者のブラウザにこのコードが表示されることはありません。つまり 499 は「エラー画面が出る問題」ではなく「ログにだけ現れる兆候」です。 原因は、クライアントが先に諦めた理由が何か、という問いに置き換えられます。典型は3つで、上流の応答が遅くクライアント側の制限時間が先に切れた、利用者が操作を中断した(画面を閉じた・再読み込みした)、Nginx の手前にいる中継役(ロードバランサーなど)や監視の仕組みが応答を待たずに切断した、のいずれかです。切り分けの鍵は処理時間の記録ですが、既定のログ形式には含まれないため、まず $request_time をログに加えるところから始めます。 エラーの概要 Nginx のソースコードには、499 を定義した箇所に説明が書かれています。要約すると、リクエストの処理中にクライアントが接続を閉じた場合を表すコードが HTTP には定義されていないため、応答ヘッダーを送ろうとする前にクライアントが接続を閉じていた状況を記録する目的で、独自のコードとして導入した、という内容です。 504 Gateway Timeout との対比で理解すると分かりやすいコードです。上流の応答が遅いとき、Nginx 側が待ちきれずに打ち切ればクライアントに 504 が返ります。Nginx がまだ待っている間にクライアント側が先に切れば、誰にも何も返らず、ログに 499 が残ります。同じ「遅い」という状況でも、どちらが先に諦めたかで記録が変わります。 診断上の重要な特徴が2つあります。第一に、499 はエラーログには info レベルでしか記録されません。エラーログの既定のレベルでは何も出力されないため、「アクセスログに 499 があるのにエラーログに手がかりがない」のは正常な動作です。第二に、既定では、クライアントの切断を検知した時点で Nginx は上流への接続も閉じます(この動作は後述の proxy_ignore_client_abort で変えられます)。ただし上流のアプリケーションが切断を検知しない作りの場合、応答の届け先がないまま処理だけが完走することもあります。 アクセスログには次のように記録されます。応答を送っていないため、送信バイト数が 0 になるのが典型です。 192.168.1.100 - - [09/Jul/2026:14:22:10 +0900] "POST /api/report HTTP/1.1" 499 0 "-" "python-requests/2.31.0" まず最初に:処理時間をログに出す 既定の combined 形式には、リクエストの処理にかかった時間が含まれません。499 の切り分けには時間の情報が不可欠なので、log_format に $request_time(リクエスト全体の処理時間)と $upstream_response_time(上流の応答にかかった時間)を加えます。どちらも公式ドキュメントに記載のある変数です。 http { log_format timed '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" ' 'rt=$request_time urt=$upstream_response_time'; access_log /var/log/nginx/access.log timed; } 反映後に記録される 499 の行を見て、次のように読み分けます。 ...