Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】nginx 504 Gateway Timeout with zero 429s from limit_req

2026年10月14日に「Dev.to」が公開したITニュース「nginx 504 Gateway Timeout with zero 429s from limit_req」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

nginxのリクエスト数制限機能は、アプリケーションサーバーの処理遅延や能力低下を検知できない。そのため、サーバーが処理能力を超えるとリクエストが滞留し、504エラー(タイムアウト)が増加する。対策として、nginxの同時接続数制限設定を活用すると、アプリケーションサーバーへの負荷を適切に保ち、504エラー発生を防げる。

ITニュース解説

Nginxを運用していると、本来はアクセス過多の際に表示されるはずの429エラー(Too Many Requests)が出ずに、504 Gateway Timeoutというエラーが急増するという奇妙な現象に遭遇することがある。これはなぜ起こるのだろうか。

まず、Nginxがどのようにリクエストを処理しているかから説明する。Nginxは、ユーザーからのリクエストを受け取ると、そのリクエストをバックエンドにある実際のアプリケーションサーバー(これを「アップストリーム」と呼ぶ)に転送する。Nginxはアップストリームからの応答を一定時間待つように設定されており、この待ち時間はproxy_read_timeoutという設定で決められる。もしこの時間内にアップストリームから応答がなければ、Nginxは処理をあきらめ、ユーザーには「504 Gateway Timeout」というエラーを返す。このとき、Nginxのアクセスログには、リクエストを受け取ってから応答を返すまでの時間を示す$request_timeが、proxy_read_timeoutで設定した値と全く同じになるという特徴がある。また、Nginxのエラーログには「アップストリームが応答ヘッダーを読み取り中にタイムアウトした」という内容が記録される。

この状況で不思議なのは、Nginxにはlimit_reqという機能があり、これはIPアドレスごとに1秒あたりのリクエスト数を制限するものだ。通常、この制限を超過した場合には「429 Too Many Requests」というエラーが返されるはずだが、この現象では429エラーが全く発生しない。さらに、個々のIPアドレスからのリクエスト数は普段と変わらないにもかかわらず、504エラーだけが急増するのだ。そして、アップストリーム自体は停止しておらず、ログも引き続き流れている。つまり、アップストリームはリクエストを処理し続けているにもかかわらず、Nginxはすでに504エラーを返しているため、アップストリームがせっかく生成した応答は誰にも届いていない状態になっている。

この問題の根源は、limit_reqがどのような基準でリクエストを制限しているかにある。limit_reqは、IPアドレスなどの「キー」ごとに、1秒あたりの平均リクエスト速度(rate)と、一時的に許容される超過量(burst)を設定する。リクエストがrateより速く到着すると、その分が「超過量」として記録され、この超過量がburstを超えた場合にのみ429エラーが返される。しかし、limit_reqは、アップストリームでリクエストがどれくらいの処理時間を要するか、あるいはアップストリームで現在どれくらいのリクエストが処理中であるかといった、アップストリーム側の負荷状況に関する情報を一切考慮しない。重いリクエストも軽いリクエストも、一律に「1つのリクエスト」としてカウントするだけだ。また、rateやburstといった設定値はNginxの起動時に固定され、稼働中にアップストリームの負荷状況に応じて動的に変化することはない。

加えて、limit_reqはIPアドレスごとに制限を適用するため、もしリクエストを送ってくるIPアドレスの総数が増加したとしても、個々のIPアドレスからのリクエスト数がlimit_reqの制限内であれば、Nginxはすべてのリクエストをアップストリームに転送してしまう。結果として、アップストリームへの総リクエスト数が大幅に増加し、アップストリームの処理能力を超過してしまうことがある。

実際に、アップストリームが処理能力の限界を超える状況をシミュレーションすると、以下のようなケースで504エラーが多発し、成功する応答がゼロになることが分かった。ここで想定するアップストリームは、同時に処理できるリクエスト数に上限があり、それを超える接続はOSの「acceptキュー」という場所で待機する仕組みだ。 一つ目は、処理に時間のかかる「重いリクエスト」の割合が増加した場合。Nginxのlimit_reqはリクエストの重さを区別しないため、見かけ上のリクエスト数が変わらなくても、アップストリームの実際の処理能力は低下する。 二つ目は、アップストリーム自体の処理時間が長くなった場合。これは、アップストリームのプログラムの性能低下や、裏で利用しているデータベース、外部サービスなどの遅延が原因で発生する。 三つ目は、アップストリームを構成するサーバーや、同時にリクエストを処理できるワーカーの数が減少した場合。これにより、アップストリーム全体の同時処理能力が低下する。 そして四つ目は、リクエストを送ってくるIPアドレスの数が増加した場合。個々のIPアドレスはlimit_reqの制限内に収まっていても、全体としてのリクエスト総数がアップストリームの処理能力を超過する。

これらの条件下で発生するメカニズムは以下の通りだ。アップストリームが処理できる以上のリクエストがNginxから送られてくると、アップストリームのacceptキューに処理待ちの接続がどんどん溜まっていく。キューに接続が溜まり続けると、後から来たリクエストほど待機時間が長くなる。そして、このacceptキューでの待機時間と、実際にアップストリームでリクエストが処理される時間の合計が、Nginxが設定しているproxy_read_timeoutを超えてしまうと、Nginxはそのリクエストの応答を待つのをあきらめ、ユーザーに504エラーを返す。同時に、Nginxはアップストリームとの接続も閉じてしまう。しかし、多くのアップストリーム、特に接続が切断されたことを検知して処理を中止するような作りになっていない場合、アップストリームはそのことに気づかない。acceptキューから接続が取り出されると、アップストリームはそれを処理し始め、最終的には応答を生成する。しかし、Nginxはすでにその接続を閉じて504を返しているので、アップストリームがせっかく作った応答は誰にも届かないのだ。これが、アップストリームのアクセスログには処理が完了した記録が残っているのに、Nginxからは200番台の正常な応答が一つも返ってこない理由である。アップストリームは無駄な処理に全力を使い続けることになる。

このような状況が疑われる場合、システムのログを調査することで原因を特定できる。まず、Nginxの504エラーが増加し、429エラーがゼロのときは、アップストリームのacceptキューでの待機時間が伸びているかを確認する。Nginxのアクセスログに記録される$upstream_header_timeは、Nginxがアップストリームに接続を開始してから応答ヘッダーを受け取るまでの時間で、これにはacceptキューでの待機時間とアップストリームでの処理時間の両方が含まれる。一方、アップストリームのアクセスログに記録される処理時間は、アップストリームが接続を受け付けてから応答を返すまでの時間であり、acceptキューでの待機時間は含まれない。この二つの時間の差が秒ごとに増えていくようであれば、acceptキューに待機が溜まっている証拠であり、この差は504エラーが出始める前から確認できる。

次に、具体的な原因を特定するために、以下の順でログを確認するとよい。 第一に、アップストリームのアクセスログで、各エンドポイントの処理時間が通常より長くなっていないかを確認する。もし長くなっていれば、アップストリームやその背後にあるデータベース、外部APIが遅延している可能性がある。 第二に、もし処理時間が変わっていなければ、Nginxのアクセスログで、処理時間の長いエンドポイントのリクエストが全体のどれくらいの割合を占めるかを見る。その割合が増加していれば、重いリクエストが増加していることが考えられる。 第三に、エンドポイントの割合も変わっていない場合は、Nginxのアクセスログで全体の1秒あたりのリクエスト数が増加していないかを確認する。もし増えていれば、リクエストを送ってくるIPアドレスの数が増えているのか、個々のIPアドレスからのリクエスト数が増えているのかをさらに分析する。IPアドレスの数が増加していれば、それが原因だ。 最後に、これら全てに当てはまらない場合は、アップストリームを構成するサーバーやワーカーの数が通常より減少していないかを確認する。これにより、アップストリームが同時に処理できるリクエスト数が減っている可能性がある。

このような問題に対する応急処置として、Nginxのmax_conns設定を利用する方法が有効だ。これは、Nginxがアップストリームに同時に送る接続の最大数を制限するもので、アップストリームが同時に処理できるリクエスト数と同じ値を設定する。zoneという設定をすることで、複数のNginxワーカー間でこの制限を共有できる。max_connsを設定すると、その制限を超えたリクエストはNginxからアップストリームに送られることなく、Nginxは即座に「502 Bad Gateway」というエラーを返すようになる。これにより、アップストリームはmax_connsの範囲内のリクエストだけを処理すればよくなるため、acceptキューに接続が溜まり続けることがなくなり、無駄な処理も発生しなくなる。ユーザーは、処理可能なリクエストであれば正常な応答を素早く受け取ることができ、処理できない分はすぐに502エラーを受け取る形になる。これにより、すべてのユーザーが長いタイムアウトを待たされるという最悪の事態は避けられる。

ただし、max_connsはあくまで被害を食い止める一時的な対策であり、根本的な原因を解決するものではない。例えば、アップストリームの処理時間が長くなったのであれば、その原因となっているデータベースや外部APIの遅延を解消する必要がある。重いリクエストが増加したのであれば、その処理を別のシステムに移したり、非同期で処理するような設計変更を検討する必要があるだろう。同時処理能力が落ちたのであれば、サーバーやワーカーを増やすべきだ。リクエストを送ってくるIPアドレスが増加して全体の負荷が増えたのであれば、それに見合うアップストリームの処理能力を増強する必要がある。

まとめると、Nginxのlimit_reqはIPアドレスあたりのリクエスト数を制限するが、アップストリームの処理能力の変化には対応できない。その結果、処理できないリクエストがアップストリームのacceptキューに溜まり、Nginxが設定したタイムアウトを超えると504エラーが発生する。このとき、アップストリームは無駄な処理を続けてしまう。この問題を解決するためには、Nginxのmax_conns設定を使って、Nginxがアップストリームに送る同時リクエスト数を制限するのが有効だ。これにより、処理可能なリクエストは正常に完了し、処理できないリクエストはすぐに502エラーとして返されるようになる。しかし、これは一時的な対処であり、根本的な原因を突き止め、それを解決することが重要となる。このメカニズムは、アップストリームが同時処理能力を超えた接続をOSのキューで待機させ、かつ接続が切断されても処理を中止しない設計の場合に特に当てはまる。

関連コンテンツ

関連IT用語