ByteScope

502

HTTP 502 Bad Gateway の直し方

5xx サーバーエラーIETF 標準RFC 9110

アプリケーションの手前にいるプロキシが、代わりに返した答えです。アプリケーション自身の応答が来なかったか、途中で切れたか、HTTP として成立していなかったことを意味します。

502 が出ている理由と、やるべきこと

同じ 3 桁でも、リクエストのどちら側にいるかで別々の問題になります。自分に当てはまるブロックを読んでください。

curl で 502 を再現する

失敗した URL に対してそのまま実行してください。エラーページを表示せずにステータスだけを出すので、ブラウザが描画したものではなくサーバーが言ったことが見えます。

curl
curl -sS -o /dev/null -D - -w '\n%{http_code} in %{time_total}s\n' https://example.com/api/

`-sS` は進捗表示を消しつつエラーメッセージは残し、`-o /dev/null` は HTML のエラーページを捨て、`-D -` は応答ヘッダーを標準出力に出し、`-w` は転送が終わったあとにステータスと実時間を出します。この出力の中の 3 つが、次にどこを見るかを決めます。ステータス行は、それが本当に 502 であって、アプリケーション自身が返した 500 ではないことを確定させます。ヘッダーの塊は書き手を名指しします——`via` の無い `server: nginx` は自前のプロキシ、`cf-ray` を伴う `server: cloudflare` は Cloudflare で、その `cf-ray` の値はサポートが聞いてくる ID です。そして経過時間が 504 との決め手になります。拒否された、あるいは壊れた upstream はミリ秒で返り、時間切れのほうはタイマーどおりに返る——nginx の既定の `proxy_read_timeout` なら 60 秒、Cloudflare が自前の 524 を出すまでなら 125 秒です。オリジンではなくプロキシ側の問題だと示すには、同じコマンドに `--resolve example.com:443:203.0.113.10` を付けて、元のホスト名と SNI を送りながら指定したオリジンのアドレスへ直接つないでください。

502 が実際に意味していること

RFC 9110 §15.6.3 は 502 を、ゲートウェイまたはプロキシとして動作しているサーバーが、リクエストを果たすためにアクセスした内側のサーバーから不正な応答を受け取ったときに返すステータス、と定義しています。この書きぶりから 2 つのことが導かれ、どちらも「どこを見るか」を変えます。1 つ目、このコードを書いているのは中間にいる側——nginx、HAProxy、Envoy、ロードバランサー、Cloudflare——であって、あなたのアプリケーションではありません。アプリケーションは一度も動いていない可能性すらあります。2 つ目、「不正な応答」は「エラー応答」よりずっと広い括りです。TCP 接続の拒否、応答ヘッダーが揃う前の切断、プロキシが解釈できないステータス行、プロキシのバッファを超える大きさのヘッダー——これらは全部あてはまります。一方、アプリケーションが自分で 500 を返しきった場合はあてはまりません。それは正しい応答なので、プロキシはそのまま素通しします。502 はタイムアウトでもありません。RFC 9110 §15.6.5 は「時間内に応答が来なかった」ほうを 504 に割り当てています。この区別にはストップウォッチが付いてきます。502 はたいていミリ秒で返り、504 は必ず何かのタイマーがちょうど切れたところで返ります。最後に、502 は RFC 9110 §15.1 のヒューリスティックにキャッシュ可能なコードの一覧に入っていないので、中間装置が独断で保存することはありません。nginx は既定で `error` と `timeout` のときに次の upstream へ回しますが、`proxy_next_upstream` に `non_idempotent` を明示しない限り、非冪等なリクエストは回しません。

502 と間違えられやすいコード

紛らわしいコード見分け方
504どちらもプロキシが upstream について報告しているもので、線引きは RFC 9110 が済ませています。502 は不正な応答、504 は時間内に来なかった応答です。文言よりも時計を読んでください。502 はたいてい 1 秒どころではない速さで返ります。upstream が接続を拒否したか、途中で切ったからです。504 はきりのいい数字で返ります——nginx の既定の `proxy_read_timeout` なら 60 秒、Cloudflare なら 125 秒。何かが座ってタイマーを待ち切ったからです。
500500 はアプリケーションが自分の失敗を認めたもの、502 はアプリケーションが使える答えを渡してこなかったとプロキシが報告したものです。フレームワークのエラーハンドラーが走ったならステータスは 500 で、そのログに例外が残り、プロキシはその応答を素通しします。502 はハンドラーが走る前にワーカーが死んだ場合が多く、アプリケーションのログは空っぽで、何かが起きた記録はプロキシのログにしか残りません。
503503 は「一時的に処理できない」で、たいていは意図的です——メンテナンスモード、ヘルスチェックがバックエンドをローテーションから外した、レートリミッターが効いた。RFC 9110 は 503 が `Retry-After` を伴うことを認めていますが、502 にそういう慣習はありません。つまり 503 は誰かがあなたを断ると決めたということ、502 はそもそも答えが返せなかったということです。
521521 は IETF ではなく Cloudflare 自身のコードで、まさにこのケースが一般的な 502 として報告されないために存在します。Cloudflare からオリジンへの接続がはっきり拒否された、という意味です。502 ではなく 521 が出るということは、問題がオリジンの待ち受けかファイアウォールに一手で絞り込めるということで、それが 520 番台がそもそも作られた理由です。
nginxnginx はクライアントに 502 を書く前に、自分のエラーログに理由を書きます。その 1 行は 3 桁の数字よりはるかに具体的です。`connect() failed (111: Connection refused)`、`upstream prematurely closed connection`、`upstream sent too big header`、`no live upstreams` は 4 つの別々の問題ですが、ブラウザにはまったく同じ顔で届きます。
理由句
Bad Gateway
クラス
5xx サーバーエラー
定義
RFC 9110
標準
IETF 標準

このサイトのツール

どれもブラウザ内で完結します。アップロードは発生しません。

関連するステータスコード

502 を調べているとき、ついでに読むことになりがちなコードです。

よくある質問

このステータスコードを出したのがどのサーバーかを知るには?

ページではなく応答ヘッダーを読んでください。curl -sS -o /dev/null -D - https://example.com/path が本文なしでヘッダーだけを出し、serverviacf-ray の 3 つで、答えたのがどの層かが分かります。cf-ray の値があれば応答を扱ったのは Cloudflare で、その値はサポートが聞いてくる ID です。エッジを完全に外して確かめたいなら、--resolve example.com:443:203.0.113.10 を付けて同じリクエストを投げ直します。元のホスト名と SNI を送りつつ、指定したオリジンのアドレスへ接続するので、答えが変われば、エッジとオリジンの言い分が食い違っているということです。

数字のあとの理由句(Not Found など)に意味はありますか?

ありません。分岐条件にしてはいけません。RFC 9112 はクライアントに理由句を無視するよう求めていますし、サーバーは自由に変えられますし、HTTP/2 と HTTP/3 にはそもそも理由句が存在しません。HTTP/1.1 では 404 Not Found として届くものが、HTTP/2 では素の 404 として届きます。このサイトが登録済みの理由句を載せているのは、それが検索される語であり HTTP/1.1 のログに出る文字列だからで、何かのソフトウェアがそれに依存しているからではありません。

このコードで失敗したリクエストは再試行すべきですか?

5xx と 429 は再試行してよく、4xx はしないでください。次に投げても中身は同じだからです。応答に Retry-After が付いていればそれに従ってください。RFC 9110 がまさにこのために定義していて、値は秒数でも日時でも構いません。付いていなければ、ジッター付きの指数バックオフと明確な上限を使い、しかも冪等なメソッドに限ってください。再試行された POST はカードに二重で課金しかねません。すでに失敗しているサーバーへの再試行の嵐は、短い障害を長い障害に変える典型的な経路です。

自分のサーバーが返すステータスコードは変えられますか?

変えられます。守るべき規則は 1 つだけ、そのコードが本当であること。応答を自動で読むもの——検索エンジン、監視、キャッシュ、クライアント側の再試行ロジック——はすべて数字だけで判断し、ページの中身は一切見ません。エラーページを 200 で返せば自分の監視から障害が隠れますし、無いページを 200 で返せばエラーが本文として索引されますし、単に不正なリクエストに 500 を返せば、当番の人をスタックの間違った半分へ送り込むことになります。

502 でまだ詰まっているなら、ステータスコード一覧をすべて見る。あるいは上に戻って、自分の立ち位置に向けて書かれたブロックを読んでください。