ByteScope

400

HTTP 400 Bad Request の直し方

4xx クライアントエラーIETF 標準RFC 9110

サーバーがリクエストの解釈自体を拒みました。リクエスト行・ヘッダー・メッセージの区切り・ボディのどこかが壊れているか大きすぎるということで、たいていの場合、アプリケーションのコードは 1 行も走っていません。

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

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

curl で 400 を再現する

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

curl
curl -sS -D - -o /dev/null -H "Cookie: pad=$(head -c 20000 /dev/zero | tr '\0' 'a')" https://example.com/

これは巨大な `Cookie` ヘッダーを 1 本だけ送ります。20,000 バイト、nginx の `large_client_header_buffers` が既定で許す 8k をゆうに超える大きさなので、nginx を既定のまま動かしている相手なら、公開ウェブでいちばんよくある 400 を待たずに意図的に再現できます。`-D -` が応答ヘッダーを出し、`-o /dev/null` がエラーページを捨て、ステータス行が結果です。読み方は 3 つ。まず `-H` を外して実行し直す。200 になるなら、リクエストの形は正しくサイズだけが問題だったということで、これは構文ではなくバッファの話です。次にバイト数を半分ずつ減らして 400 が 200 に変わる点を探せば、そのサーバーの実際のヘッダー上限が測れます。これが `large_client_header_buffers` と突き合わせるべき数字です。逆に 1 回目から 200 が返ってきた場合、コマンドが間違っているのではありません。手前にある上限が送った量より単に大きいだけで、実際そういうエッジもあります。パディングを 40 000 バイトに倍増し、ステータスが変わるまで倍にし続けてください。ここから先の読み方はどれも、400 を実際に見たことを前提にしています。そしてダンプの `server:` と `cf-ray` の有無から、拒否したのが誰かを読みます。エッジとオリジンで上限が違うことがあり、自分の設定に書いてあるのはそのうち片方だけだからです。もう一つの大きな系統には `curl -v http://example.com:443/` を使います。TLS のポートに平文 HTTP を送るもので、nginx はこれに「The plain HTTP request was sent to HTTPS port」というボディの 400 で答えます。この一文を知っているだけで、何も書かれていないアプリケーションログを読む 1 時間が浮きます。

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

RFC 9110 §15.5.1 は 400 を、クライアント側の誤りと受け取れる何かのせいでサーバーがリクエストを処理しない状態と定義し、例として壊れたリクエスト構文・不正なメッセージフレーミング・欺瞞的なリクエストルーティングの 3 つを挙げています。この一文は、住んでいる建物がまったく違う 2 種類の失敗をまとめて指していて、その 2 つを切り分けることが作業のほとんどです。プロトコル階層の 400 は、ハンドラが呼ばれる前にサーバーか手前のエッジが書きます。RFC 9110 §7.2 は、`Host` フィールドの無い HTTP/1.1 リクエスト、複数持つリクエスト、不正な値を持つリクエストに 400 を返すことをサーバーに要求していますし、RFC 9112 §6.3 は `Content-Length` と `Transfer-Encoding` を両方運ぶメッセージをリクエストスマグリングに直結する誤りと定めていて、堅く作られたサーバーは推測せずに拒否します。もう一方、アプリケーション階層の 400 は、パースには成功したボディをあなた自身のハンドラが突き返したものです。前者はアプリケーションのログに 1 行も残らず、後者は残る——この非対称が、いま使える中でいちばん速い判定です。なお RFC 9110 は後者向けにもっと鋭いコードを用意しています。§15.5.21 の 422 (Unprocessable Content) は、構文は正しいのに指示に従えない内容のためのもので、世の「バリデーション失敗」応答の実態はたいていこちらです。

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

紛らわしいコード見分け方
422400 はサーバーが解釈できなかったリクエストのため、422 (Unprocessable Content, RFC 9110 §15.5.21) は完全に解釈できたのに実行できない内容のためのコードです。スキーマ検証に落ちた JSON ボディはパース自体は成功していて、問題は構文ではなく意味です。422 ならクライアントに「ペイロードは無傷で届いた、値が違う」と伝わり、「リクエストが壊れていた」とは別の対処になります。
414どちらもサイズの上限で、最初の改行のこちら側とあちら側です。nginx は、バッファより長い*リクエスト行*は 414 (URI Too Long, §15.5.15)、長すぎる*ヘッダーのフィールド行*は 400 と説明しています。だから 400 は Cookie とヘッダーを指し、414 は誰かがループで組み立てたクエリ文字列を指します。どちらを縮めるべきかは、コードだけで分かります。
431431 (Request Header Fields Too Large, RFC 6585 §5) は、まさに Cookie 肥大のために存在するコードで、400 よりずっと多くを語ります。ただし nginx はこれを使わず、「Request Header Or Cookie Too Large」というボディを付けた 400 で答えます。公開ウェブで 431 を見かけないことは、ヘッダーが原因かどうかについて何の情報にもなりません。
nginxnginx はクライアントに 400 を書く前に、本当の理由を自分のエラーログへ書いています。ステータスコードが曖昧なぶん、こちらの文言は具体的です。`client sent too long header line` はサイズ、`client sent invalid method while reading client request line` は構文、そして内部コード 497——TLS ポートに届いた平文——は、まぎれもないボディを付けた 400 としてクライアントに出ます。ログ 1 行が、当て推量の午後を丸ごと置き換えます。
理由句
Bad Request
クラス
4xx クライアントエラー
定義
RFC 9110
標準
IETF 標準

このサイトのツール

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

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

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

よくある質問

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

ページではなく応答ヘッダーを読んでください。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 を返せば、当番の人をスタックの間違った半分へ送り込むことになります。

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