結論

400 Bad Request は「サーバーがリクエストを読めなかった(形がおかしい)」という意味で、直すのは送った側です。見ている人は URL の確認と Cookie の削除、作る人は「誰が400を返したか」を curl -v とログで突き止めるのが近道です。

この記事でわかること

  • 400になる原因9種と、見る人・作る人別の直し方
  • nginx・Express・FastAPI で実際に返した400の curl -v 原文と error.log の文言
  • 400と 413・414・415・422・431 の使い分けと、Problem Details・ログの書き方

2026年10月29日更新|nginx 1.24・Express 5.3・FastAPI 0.162・Chromium 153 で実行確認

サーバーは、届いたリクエストを「大きさは上限内か」「形式を受け付けるか」「構文として読めるか」「内容は決まりに合うか」の順に見ます。「読めない」が400で、それ以外の段で止まると 413(本文が大きい)・414(URL が長い)・431(ヘッダーが大きい・Node.js)・415(形式)・422(内容)のように別の番号になります。nginx はヘッダーの超過も400で返します。

flowchart TD
    R[リクエストが届く] --> A[大きさが上限を超えた?<br>→ 413 本文 / 414 URL / 431 ヘッダー]
    A -->|上限内| C[形式を受け付けない?<br>→ 415]
    C -->|受け付ける| S[構文として読めない?<br>→ 400 Bad Request]
    S -->|読める| V[内容が決まりに反する?<br>→ 422 または 400]
    V -->|合っている| OK[200 OK]

よく調べられることの答えを先にまとめます。

知りたいこと 答え 詳しい節
http error 400 とは サーバーがリクエストを「不正な形」と判断して処理しなかった 400 Bad Requestとは
400 bad request の直し方(見る人) URL を確かめる→再読み込み→Cookie を消す→別のブラウザで開く 400エラーが出たときの直し方
Request Header Or Cookie Too Large Cookie を含むヘッダー1行が nginx の 8KB を超えた。Cookie を消すと直る Cookieが大きすぎる400
400 エラーの原因(作る人) 壊れた JSON・必須パラメーター不足・ヘッダー超過・URL の文字 400エラーの原因一覧
400と422の使い分け 構文として読めなければ400、読めたが内容が決まりに反すれば422 必須パラメーター不足と422
400と414の違い URL(リクエスト行)が長すぎると414、ヘッダー1行が長すぎると400 URLが長すぎる400と414
Content-Type を間違えた 形式そのものを受け付けないなら415。読めない本文なら400 Content-Typeの不一致
400の本文に何を書くか Problem Details(RFC 9457)で、どの項目がなぜ誤りかを返す 400の本文とログ

確認環境:Ubuntu 24.04 の上で、nginx 1.24.0(Ubuntu)を自分の設定ファイルで 127.0.0.1 のポートに起動し、大きすぎる Cookie・長すぎる URL・壊れたリクエスト行・不正な Content-Length・壊れたパーセントエンコードで400と414を返しました。背後の API は Express 5.3.0(Node.js 22.23.4)と FastAPI 0.162.0(Starlette 1.10.0・pydantic 2.14.1・uvicorn 0.54.0・Python 3.13.18)で作り、nginx から中継しています。応答は curl 8.5.0 の curl -v と、生のバイト列を送るときは nc で確かめ、ブラウザの表示は Chromium 153.0.8010.12(playwright 1.63.0・英語の画面)で確かめました。400・413・414・415・422 の定義は MDN(RFC 9110 を出典とする解説)、nginx の上限は nginx の公式ドキュメント、Express の本文の読み取りは body-parser の公式ドキュメント、FastAPI の既定の動きは公式ドキュメント、検索への影響は Google 検索セントラルで確かめました。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「400 Bad Requestとは|HTTP error 400の原因と直し方・実例」

400 Bad Requestとは(意味と、誰が返しているか)

この節では、400の正確な意味と、「誰が400を返したか」を最初に見分ける理由が分かります。

400 Bad Request は、サーバーがクライアントの誤りとみなした理由で、リクエストを処理しなかったことを示すステータスコード(サーバーが返す3桁の結果番号)です。 MDN は次のように説明しています。

400 レスポンスの理由は、通常、リクエスト構文が不正であったり、リクエストメッセージのフレームが不正であったり、リクエストルーティングが不正であったりするものです。

つまり「サーバーが壊れている」のではなく、「送られてきたものの形がおかしい」のが400です。MDN は、400を受け取ったクライアントは、リクエストを変えずに繰り返しても同じエラーで失敗すると想定すべきだ、とも書いています。何度も再読み込みしても直らないのはこのためです。

400は、アプリケーションに届く前の層でも返されます。手元では、nginx(Web サーバー)・Node.js(Express の土台)・Express の本文の読み取り・FastAPI の入力チェック、の4か所がそれぞれ別の文言で400を返しました。直す場所が違うので、最初に「どの層が返したか」を見分けます。

1

Web サーバー(nginx)

ヘッダーが8KBを超えた・リクエスト行が壊れている。本文は nginx の HTML。アプリのログには残らない。

error.log を見る

2

言語の HTTP サーバー(Node.js)

ヘッダーが16KBを超えると、Express に渡る前に Node が 431 を返した。

Express のログに残らない

3

本文の読み取り(body-parser)

JSON が壊れている・文字コードが未対応。err.type で原因が分かる。

エラーハンドラーで捕まえる

4

アプリの入力チェック

必須項目が無い・型が違う。自分で400か422を決める。

どの項目が誤りかを本文に書く

400エラーの原因一覧(見る人・作る人別)

この節では、400になる原因を「見ている人の操作で直るもの」と「サイトや API を作る側で直すもの」に分けて一覧で確認できます。

原因 よく出る場面 直す人 手元で確かめた
Cookie を含むヘッダーが大きすぎる 長く使ったブラウザ・同じドメインの複数サービス 見る人(Cookie 削除)/作る人(上限・設計) nginx
URL に使えない文字・壊れた %エンコード 手で打った URL・コピーで欠けた URL 見る人/作る人 nginx・Express
JSON の構文が壊れている 末尾のカンマ・シングルクォート 作る人(送信側) Express・FastAPI
必須パラメーターが無い・型が違う API の仕様変更・フォームの未入力 作る人(送信側) Express・FastAPI
Content-Type が本文と合わない curl の既定(フォーム形式)で JSON を送った 作る人(送信側) Express(415)・FastAPI(422)
リクエスト行・ヘッダーの文法違反 HTTP のポートに HTTPS で接続・Host が無い 見る人(URL の https)/作る人 nginx
Content-Length が不正・重複 自作のクライアント・中継の誤り 作る人 nginx
ブラウザの拡張機能がヘッダーを書き換えた 特定のブラウザでだけ出る 見る人(拡張機能を切る) 手元では試していない(切り分けの手順のみ)
Google ログインの承認エラー(invalid_request・redirect_uri_mismatch) アプリのリダイレクト先や必須パラメーターの誤り 作る人(アプリの提供元) Google の公式ドキュメントで確認

見ている人の操作で直ることが多い

Cookie が溜まって大きくなった

URL の打ち間違い・コピー漏れ

拡張機能・VPN がヘッダーを変えた

作る側で直すもの

送信側のプログラムが作る JSON・パラメーター

サーバーの上限(ヘッダー・本文)と Cookie の設計

中継(プロキシ)の設定

400エラーが出たときの直し方(見ている人向け)

この節では、Web サイトを見ていて「400 Bad Request」「HTTP ERROR 400」が出たときに、自分でできることが順番に分かります。

順番は「URL → 再読み込み → Cookie → 別の環境」です。 一番多いのは Cookie が大きくなって nginx などの上限を超える場合で、そのサイトの Cookie を消すと直ります。

1

URL を確かめる

手で打った部分の綴り、末尾が欠けていないか、https:// と http:// を間違えていないかを見る。メールやチャットからコピーした URL は、途中で改行されて欠けていることがある。

1分

2

再読み込みし、別のタブで開き直す

一時的な中断で起きた400なら、これで直る。同じ画面を複数のタブで開いていたなら、他のタブを閉じてからやり直す。

1分

3

そのサイトの Cookie を削除する

Chrome(パソコン)は「設定」→「プライバシーとセキュリティ」→「サードパーティ Cookie」→「すべてのサイトデータと権限を表示」で、サイト名を検索して削除する(Google の公式ヘルプ)。手元では、Cookie を消した直後に同じ URL が 200 で開いた。

ログインし直しが要る

4

シークレットウィンドウ・別のブラウザで開く

Cookie も拡張機能も使わない状態で開ける。これで開けるなら、原因は手元のブラウザの Cookie か拡張機能にある。

切り分け

5

拡張機能を一時的に切る

広告ブロック・セキュリティ系の拡張機能はヘッダーを書き換えることがある。1つずつ戻して、どれで400になるかを見る。

特定のブラウザだけ出るとき

6

送るファイルを小さくする

アップロードで出るなら、大きさの上限に当たっている(厳密には 413 が多い)。圧縮するか、分けて送る。

アップロード時

7

サイトの管理者に知らせる

ここまでで直らなければ、サイト側の設定や不具合の可能性がある。日時・URL・何をしたか・ブラウザ名を添えて知らせると、ログから探してもらえる。

最後の手段

  • 「自分だけ400」か「全員400」かは、別の端末やスマホの回線で同じ URL を開くと分かります。 自分だけなら Cookie か拡張機能、全員なら サイト側です。
  • Google のログイン画面で「承認エラー」「invalid_request」「redirect_uri_mismatch」と出るときは、Google の公式ドキュメントによると、リクエストの形式や必須パラメーターの誤り、登録したリダイレクト先との不一致(http と https・大文字小文字・末尾のスラッシュまで一致が要る)が原因です。見ている人の操作では直らないので、アプリの提供元に知らせます。

Cookie が何をしているものかは、関連記事「Cookieとセッションの違いと仕組み|セッションIDの流れを図解」で扱っています。

Chromeでの400の見え方(HTTP ERROR 400・Bad Request)

この節では、400のときにブラウザに何が表示されるのか、表示から「誰が返したか」をどう読むかが分かります。

画面は、サーバーが本文を返したかどうかで変わります。 手元で3通りの400を返し、Chromium 153 で開いたときの表示です。

返した場所 ブラウザに見えた表示 読み取れること
nginx(Cookie が大きすぎる) 400 Bad Request/Request Header Or Cookie Too Large/nginx/1.24.0 (Ubuntu) Web サーバーが返した。Cookie を消す
nginx(壊れたパーセントエンコード) 400 Bad Request/nginx/1.24.0 (Ubuntu) Web サーバーが返した。URL を見直す
Express(JSON の本文つき) {"type":"about:blank","title":"page は1以上の整数で指定します","status":400,…} アプリが返した。本文に理由がある
Express(本文の無い400) This page isn’t working/If the problem continues, contact the site owner./HTTP ERROR 400 ブラウザが代わりに出した画面
  • 「HTTP ERROR 400」の画面は、サーバーが本文の無い400を返したときに Chrome が代わりに出すものです。 手元の Chromium は英語の画面でしたが、日本語の Chrome では「このページは動作していません」と表示されます(同じ環境で確かめた401の表示は、関連記事「401と403の違い|Unauthorized・Forbiddenの原因と直し方」にあります)。
  • 画面の下に nginx などのソフトの名前があれば、そのソフトが返した400です。JSON が見えるなら、アプリが返しています。
  • Chrome では F12 の「ネットワーク」タブで、該当の行の「ステータス」が 400 かどうか、「ヘッダー」の「Cookie」がどれだけの長さかを見られます。

Cookieが大きすぎる400(Request Header Or Cookie Too Large)

この節では、nginx がヘッダーの大きさで400を返す仕組み、上限が「何バイト」なのかの実測、直し方が分かります。

nginx は、リクエストヘッダー1行が large_client_header_buffers の1つ分(既定 8KB)に収まらないと400を返します。 公式ドキュメントは「リクエスト行は1つのバッファを超えられず、超えると 414。ヘッダーの1フィールドも同様で、超えると 400」と説明しています。Cookie は複数あっても1行の Cookie: ヘッダーにまとめて送られるので、Cookie の合計が上限に当たりやすいのです。

9,000文字の Cookie を送った curl -v の原文です(値は省いています)。

$ curl -v -H "Cookie: session=aaa…(9000文字)" http://127.0.0.1:46001/
> GET / HTTP/1.1
> Host: 127.0.0.1:46001
> User-Agent: curl/8.5.0
> Accept: */*
> Cookie: session=aaa…(9000文字)
>
< HTTP/1.1 400 Bad Request
< Server: nginx/1.24.0 (Ubuntu)
< Content-Type: text/html
< Content-Length: 242
< Connection: close
<
<html>
<head><title>400 Request Header Or Cookie Too Large</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>Request Header Or Cookie Too Large</center>

error.log には、どのヘッダーが長すぎたかが残りました。

2026/10/29 17:30:58 [info] 2015#2015: *2 client sent too long header line: "Cookie: session=aaa…" while reading client request headers, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "127.0.0.1:46001"

上限は「ヘッダー1行が 8,192 バイト」だった

Cookie の値の長さを1バイトずつ変えて、どこで400になるかを測りました。

8,180字値がここまでなら 200「Cookie: s=」10字+改行2字で 8,192
8,181字1字増えると 400Request Header Or Cookie Too Large
10,000字2行に分けると 200Cookie: 5,000字×2行は通った
16k設定を 4 16k にすると 2009,000字の Cookie が通った

「Cookie: 」の名前部分と行末の改行を含めて 8,192 バイト(8KB)ちょうどが境目でした。 ブラウザは Cookie を1行にまとめて送るので、1つ1つは小さくても、合計が 8KB に近づくと400になります。手元の Chromium で 3,000文字の Cookie を3つ置いた(document.cookie.length は 9,019)だけで、トップページが400になりました。

直し方(作る側)

直し方 書き方 位置づけ
バッファを広げる large_client_header_buffers 4 16k;(http か server に書き、nginx -t → reload) 応急処置。手元では 9,000字の Cookie が 200 になった
Cookie を小さくする セッション ID だけを Cookie に置き、中身はサーバー側に持つ。JWT のクレームを減らす 根本の対処
Cookie を減らす 使っていない Cookie に有効期限切れを返して消す。Domain を広げすぎない 同じドメインの別サービスの Cookie も一緒に送られる
Cookie の合計を監視する ログに $http_cookie の長さを残し、上限の8割で気づけるようにする 再発防止
  • 上限を広げても、Cookie が増え続ければいずれ当たります。広げるのは「原因を特定したあとの時間稼ぎ」と決めておくと、判断がぶれません。
  • nginx を通過しても、後ろの Node.js には既定 16KB の上限があり、手元では 20,000字の Cookie を Node に直接送ると 431 Request Header Fields Too Large が返りました(Express のログには残りません)。uvicorn は同じ 20,000字を受け付けました。

URLが長すぎる400と414(nginxのerror.log原文)

この節では、URL が原因の400と414の違い、URL に使えない文字で400になる例、nginx の error.log に出る文言の一覧が分かります。

URL(リクエスト行)が長すぎるときは400ではなく 414 URI Too Long です。 同じ nginx に、9,000文字のクエリ付き URL を送った結果です。

$ curl -i "http://127.0.0.1:46001/?q=aaa…(9000文字)"
HTTP/1.1 414 Request-URI Too Large
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/html
Content-Length: 186
Connection: close

# error.log
client sent too long URI while reading client request line, client: 127.0.0.1, server: , request: "GET /?q=aaa…

一方、URL の「文字」がおかしいときは400です。curl は URL に生のスペースがあると送る前に「URL rejected: Malformed input to a URL function」で止めるので、nc で生のバイト列を送って確かめました。

送ったリクエスト 応答 error.log の文言
GET /a b.html HTTP/1.1(生のスペース) 400 client sent invalid request while reading client request line
GET /a%20b.html(エンコード済み) 404(読めた) open() ... failed (2: No such file or directory)
GET /api/users/%E3%81%82%E(壊れた %) 400 client sent invalid request while reading client request line
GET /日本語.html(生の UTF-8) 404(読めた) open() ".../日本語.html" failed
GET /search?q={"a":1}(生の波かっこ) 404(読めた) open() ".../search" failed
GET / HTTP/1.1 に Host が無い 400 client sent HTTP/1.1 request without "Host" header
HELLO(HTTP ではない) 400 client sent invalid method while reading client request line
HTTP のポートに HTTPS(TLS)で接続 400 client sent invalid method ... request: "\x16\x03\x01\x00\x05..."
Content-Length: abc 400 client sent invalid "Content-Length" header
Content-Length が2行 400 client sent duplicate header line: "Content-Length: 6", previous value: "Content-Length: 5"
Bad Header: v(名前にスペース) 400 client sent invalid header line: "Bad\x20..."

1日本語や { } は400にならなかった

nginx 1.24 は生の UTF-8 と波かっこをそのまま読んだ。400になったのは、生のスペースと壊れた %エンコード。

2https を http のポートに送ると400

error.log の request に \x16\x03\x01 で始まるバイト列があれば、TLS の握手を HTTP として読んだ印。URL の https:// とポートを確かめる。

3error.log は info レベルで出る

ヘッダー超過や文法違反の400は [info] で記録された。error_log ... info; にしないと見えない。

Express に直接送ったときは、壊れた %エンコードを Express 自身が Failed to decode param '%E3%81%82%E' という400で返しました。nginx を前に置いていると、nginx の400が先に出て Express には届きません。アプリのログに該当のリクエストが無い400は、手前の層が返しています。

JSONが壊れている400(Express・FastAPIの原文)

この節では、API に送った JSON の構文が壊れているときに、Express と FastAPI が既定で何を返すのかと、エラー原文から原因を読む方法が分かります。

Express の express.json() は壊れた JSON に400を返し、FastAPI は既定で 422 を返します。 末尾にカンマが余った JSON({"name":"taro","email":"t@example.com",})を nginx 経由で送った結果です。

$ curl -v -X POST -H 'Content-Type: application/json' \
    -d '{"name":"taro","email":"t@example.com",}' http://127.0.0.1:46001/api/users
> POST /api/users HTTP/1.1
> Content-Type: application/json
> Content-Length: 40
>
< HTTP/1.1 400 Bad Request
< Server: nginx/1.24.0 (Ubuntu)
< Content-Type: application/problem+json; charset=utf-8
< X-Request-Id: 95ef27fb
<
{"type":"about:blank","title":"リクエストの本文を読めません","status":400,
 "instance":"/api/users","requestId":"95ef27fb",
 "detail":"Expected double-quoted property name in JSON at position 39 (line 1 column 40)",
 "errorType":"entity.parse.failed"}

detail の文言は Node.js の JSON.parse が出すもので、「position 39(40文字目)」が、余ったカンマの直後を指しています。body-parser の公式ドキュメントは、構文の失敗を status 400・type 'entity.parse.failed' と定めています。

送った本文 Express 5.3(既定) FastAPI 0.162(既定)
末尾にカンマ …,} 400・Expected double-quoted property name in JSON at position 39 422・json_invalid・Illegal trailing comma before end of object
シングルクォート {'name':'taro'} 400・Expected property name or '}' in JSON at position 1 422・json_invalid
ただの文字列 "hello" 400・Unexpected token '"', ""hello"" is not valid JSON(strict が既定 true) 422・model_attributes_type
正しい JSON 201 201

FastAPI の既定の応答は、detail の配列に loc(どこか)・msg・type が入ります。

HTTP/1.1 422 Unprocessable Content
{"detail":[{"type":"json_invalid","loc":["body",38],"msg":"JSON decode error","input":{},"ctx":{"error":"Illegal trailing comma before end of object"}}]}

送信側で直すときは、JSON を手で組み立てずに言語の関数(JavaScript なら JSON.stringify、Python なら json.dumps)で作ります。文字列の連結で作った JSON は、値に引用符や改行が入ったときに壊れます。

必須パラメーター不足・型違いの400と422の使い分け

この節では、「JSON としては読めたが内容が足りない・違う」ときに400と422のどちらを返すか、その線引きと実装が分かります。

線は「構文として読めたかどうか」で引きます。 読めなければ400、読めたが必須項目が無い・型が違う・業務の決まりに反するなら 422 Unprocessable Content です。MDN は422を「サーバーがリクエストコンテンツのコンテンツ型を理解し、構文が正しいことを示していますが、コンテンツに格納された指示を処理することができなかった」と説明しています。

読めない と 読めたが違う

400JSON の構文が壊れている。page=abc を整数として読めない。Content-Length が不正
↔
422JSON は読めたが email が無い。在庫を超える数量。終了日が開始日より前

どちらも「送り直すときに変えるのはクライアント」。番号の違いは、クライアントが「構文」を直すか「内容」を直すかの違い

FastAPI は既定ではどちらも422にまとめます。手元では、RequestValidationError のハンドラーで「JSON が読めない(json_invalid)ときだけ400」に分けました。

@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
    errors = exc.errors()
    # JSON として読めない(構文の誤り)→ 400、読めたが内容が決まりに合わない → 422
    parse_failed = any(e["type"] == "json_invalid" for e in errors)
    status = 400 if parse_failed else 422
    return JSONResponse(
        status_code=status,
        media_type="application/problem+json",
        content={
            "type": "about:blank",
            "title": "リクエストの本文を読めません" if parse_failed else "入力に誤りがあります",
            "status": status,
            "errors": [{"loc": e["loc"], "msg": e["msg"], "type": e["type"]} for e in errors],
        },
    )
$ curl -i -X POST -H 'Content-Type: application/json' -d '{"name":"taro","email":"t@example.com",}' http://127.0.0.1:46001/py/users
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{"type":"about:blank","title":"リクエストの本文を読めません","status":400,"errors":[{"loc":["body",38],"msg":"JSON decode error","type":"json_invalid"}]}

$ curl -i -X POST -H 'Content-Type: application/json' -d '{"name":"taro"}' http://127.0.0.1:46001/py/users
HTTP/1.1 422 Unprocessable Content
{"type":"about:blank","title":"入力に誤りがあります","status":422,"errors":[{"loc":["body","email"],"msg":"Field required","type":"missing"}]}

Express には入力チェックの仕組みが無いので、自分で書きます。手元の API は、必須項目の不足を400にし、どの項目がなぜ誤りかを errors に入れました。

app.post('/api/users', (req, res) => {
  if (!req.is('application/json')) {
    return problem(res, req, 415, 'Content-Type は application/json が必要です', { accept: ['application/json'] });
  }
  const errors = [];
  const { name, email } = req.body || {};
  if (typeof name !== 'string' || name.length === 0) errors.push({ field: 'name', message: 'name は必須です' });
  if (typeof email !== 'string' || !email.includes('@')) errors.push({ field: 'email', message: 'email の形式が正しくありません' });
  if (errors.length) return problem(res, req, 400, '入力に誤りがあります', { errors });
  res.status(201).json({ id: 1, name, email });
});
$ curl -i -X POST -H 'Content-Type: application/json' -d '{"name":"taro"}' http://127.0.0.1:46001/api/users
HTTP/1.1 400 Bad Request
{"type":"about:blank","title":"入力に誤りがあります","status":400,"instance":"/api/users","requestId":"c60fb337","errors":[{"field":"email","message":"email の形式が正しくありません"}]}

$ curl -i "http://127.0.0.1:46001/api/items?page=abc"
HTTP/1.1 400 Bad Request
{"type":"about:blank","title":"page は1以上の整数で指定します","status":400,"instance":"/api/items?page=abc","requestId":"6bf7ee5e","received":"abc"}
  • すべての入力の誤りを400にまとめる設計も広く使われています。 Ruby on Rails や Laravel は入力チェックの失敗に422を使い、Express のプロジェクトでは400にまとめることが多いです。大切なのは、プロジェクトの中で揃えて API の仕様書に書くことです。
  • クライアントから見ると、400も422も「送り直すときに自分が変える」コードです。本文の errors に「どの項目が・なぜ」を入れておけば、番号の違いで困ることはありません。

Content-Typeの不一致(415と400の違い)

この節では、Content-Type が本文と合わないときに何が起き、415と400のどちらを返すのが適切かが分かります。

「その形式は受け付けない」なら 415 Unsupported Media Type、「受け付ける形式だが中身が読めない」なら400です。 MDN は415を「メッセージのコンテンツが対応していない形式であるため、サーバーがリクエストの受け入れを拒否する」と説明しています。

curl の -d は既定で Content-Type: application/x-www-form-urlencoded を付けます。JSON を送ったつもりでも、サーバーには「フォーム形式」として届きます。

$ curl -v -X POST -d '{"name":"taro","email":"t@example.com"}' http://127.0.0.1:46001/api/users
> Content-Type: application/x-www-form-urlencoded
< HTTP/1.1 415 Unsupported Media Type
{"type":"about:blank","title":"Content-Type は application/json が必要です","status":415,"instance":"/api/users","requestId":"1dc2f758","accept":["application/json"]}
送った Content-Type Express 5.3(手元の API) FastAPI 0.162(既定)
無し(curl の既定=フォーム形式) 415(req.is() で確かめて返した) 422・model_attributes_type・Input should be a valid dictionary or object
text/plain で JSON の文字列 415(express.json は読まず req.body は空) 422・同上
application/json; charset=shift_jis 415・unsupported charset "SHIFT_JIS"(body-parser) (Python 側で読める)
application/json 201 201
  • Express の express.json() は Content-Type が application/json でない本文を読みません(type オプションの既定)。req.body が空のまま先に進むので、「必須項目が無い」という別の400に見えます。先に req.is('application/json') で確かめて415を返すと、送信側が原因に気づけます。
  • FastAPI は、本文を JSON として読めなかったとき、Content-Type に関係なく「本文が辞書ではない」という422にまとめました。

400・413・414・415・422・431の使い分け(判断表)

この節では、API を作る人が「クライアントの誤り」に返すコードを迷わず決める順番と、本文の大きさで返る 413 の実物が分かります。

  1. 1URL(リクエスト行)が長すぎる → 414nginx は large_client_header_buffers の1つ分(8KB)を超えると414
  2. 2ヘッダーが大きすぎる → 400(nginx)または 431(Node.js)Cookie の合計が原因のことが多い
  3. 3本文が大きすぎる → 413nginx の client_max_body_size(既定 1m)・express.json の limit(既定 100kb)
  4. 4受け付けない形式・文字コード → 415フォーム形式で JSON の API を呼んだ
  5. 5構文として読めない → 400壊れた JSON・不正な %エンコード・読めない整数
  6. 6読めたが内容が決まりに合わない → 422(または400)必須項目の不足・形式・範囲
  7. 7認証・権限・存在・競合 → 401・403・404・409400の仲間ではない

本文が大きすぎると 413(nginx と Express の実物)

$ curl -i -X POST -H 'Content-Type: application/octet-stream' --data-binary @two.bin http://127.0.0.1:46001/upload/   # 2MB、nginx は client_max_body_size 1m
HTTP/1.1 413 Request Entity Too Large
<head><title>413 Request Entity Too Large</title></head>

$ curl -i -X POST -H 'Content-Type: application/json' --data-binary @big.json http://127.0.0.1:46001/api/users    # 150KB、express.json の既定 100kb
HTTP/1.1 413 Payload Too Large
{"type":"about:blank","title":"request entity too large","status":413,…,"errorType":"entity.too.large"}

nginx の公式ドキュメントは、client_max_body_size を超えると 413 を返し、ブラウザはこのエラーを正しく表示できない、と注意しています。アップロードで「400 のような画面」が出るときは、実際には413のことがあります。413の理由句は、RFC 9110 で「Payload Too Large」から「Content Too Large」に変わりました(MDN)。

ケース 返すコード 補足
JSON の構文が壊れている 400 Express は既定で400、FastAPI は既定で422
必須パラメーターが無い 422 または 400 プロジェクトで揃える
クエリの page=abc を整数として読めない 400(または422) FastAPI は int_parsing の422
Cookie を含むヘッダーが上限を超えた 400(nginx)/431(Node.js) 見る人は Cookie を消す
URL が長すぎる 414 長いクエリは POST の本文へ
本文が上限を超えた 413 上限を本文に書いて返す
フォーム形式で JSON の API を呼んだ 415 受け付ける形式を本文に書く
Authorization ヘッダーの形が違う 400・error="invalid_request" RFC 6750。無い・無効なら401
同じメールアドレスで登録済み 409 400にしない

認証まわり(401・403)の返し分けは前述の401と403の記事で、全部のコードは関連記事「HTTPステータスコード 一覧|全コードの原因と対処・curl実例つき」で扱っています。

作る人向け:400の本文(Problem Details)とログの残し方

この節では、400の本文に何を書けばクライアントが直せるのか、サーバーのログに何を残せば問い合わせからたどれるのかが分かります。

400の本文は「どの項目が・なぜ・どう直すか」を機械が読める形で返します。 RFC 9457(Problem Details for HTTP APIs)の形に揃えると、クライアントのエラー処理を共通にできます。手元の Express と FastAPI はどちらも application/problem+json で返しています。

HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
X-Request-Id: c60fb337

{
  "type": "https://example.com/problems/validation-error",
  "title": "入力に誤りがあります",
  "status": 400,
  "instance": "/api/users",
  "requestId": "c60fb337",
  "errors": [
    { "field": "email", "message": "email の形式が正しくありません" }
  ]
}
  • 1構文の失敗と内容の失敗で title を分ける「本文を読めません」と「入力に誤りがあります」。クライアントは直す場所が分かる
  • 2項目ごとの errors を配列で返す最初の1件で止めず、全部返す。フォームは一度に直せる
  • 3リクエスト ID を応答ヘッダーと本文とログに入れる問い合わせの ID でログを1行に絞れる。手元は X-Request-Id を自分で付けた
  • 4内部の情報を本文に出さないスタックトレース・検証の実装の詳細・他のユーザーの存在が分かる文言は書かない
  • 5400 は「比率」で監視する件数は利用者の打ち間違いでも増える。全体に対する比率の急増と、手前の層の400を分けて見る

Express:body-parser のエラーをまとめて対応づける

body-parser は、失敗の種類を err.type に入れて投げます。公式ドキュメントのとおり、構文の失敗は400、大きすぎは413、文字コードや圧縮の未対応は415です。手元のエラーハンドラーと、残したログです。

app.use((err, req, res, next) => {
  const map = { 'entity.parse.failed': 400, 'entity.too.large': 413,
                'charset.unsupported': 415, 'encoding.unsupported': 415 };
  const status = map[err.type] || err.status || 500;
  console.log(JSON.stringify({ level: 'warn', requestId: req.id, method: req.method, url: req.originalUrl,
    status, type: err.type, message: err.message,
    contentType: req.get('Content-Type'), length: req.get('Content-Length') }));
  problem(res, req, status, status === 400 ? 'リクエストの本文を読めません' : err.message,
          { detail: err.message, errorType: err.type });
});
{"level":"warn","requestId":"95ef27fb","method":"POST","url":"/api/users","status":400,"type":"entity.parse.failed","message":"Expected double-quoted property name in JSON at position 39 (line 1 column 40)","contentType":"application/json","length":"40"}
{"level":"warn","requestId":"d01417ee","method":"POST","url":"/api/users","status":413,"type":"entity.too.large","message":"request entity too large","contentType":"application/json","length":"150039"}
{"level":"warn","requestId":"104f9c62","method":"POST","url":"/api/users","status":415,"type":"charset.unsupported","message":"unsupported charset \"SHIFT_JIS\"","contentType":"application/json; charset=shift_jis","length":"39"}

ログに Content-Type と Content-Length を残すと、「本文そのもの」を残さなくても原因の大半が分かります。本文を丸ごと残すとパスワードや個人情報が混ざるので、残すなら項目名だけにします。

nginx:400 は access.log と error.log の両方で見る

# access.log(9番目の欄がステータス)
127.0.0.1 - - [29/Oct/2026:17:30:58 +0900] "GET / HTTP/1.1" 400 242 "-" "curl/8.5.0"
127.0.0.1 - - [29/Oct/2026:17:30:58 +0900] "GET /?q=aaa…" 414 186 "-" "-"
127.0.0.1 - - [29/Oct/2026:17:31:08 +0900] "HELLO" 400 166 "-" "-"

# error.log(error_log ... info; のとき)
client sent too long header line: "Cookie: session=aaa…" while reading client request headers
client sent HTTP/1.1 request without "Host" header while reading client request headers

access.log の 400 は「何が来たか」までは分かりません。理由は error.log の client sent … の行にあります。手元では、ヘッダー超過も文法違反も [info] レベルで記録されたので、調べるときだけ error_log /path/error.log info; にして reload します。エラー本文の設計全体は、関連記事「APIエラーレスポンス設計|RFC 9457準拠のJSON例と実装コード」で扱っています。

よくあるエラー・つまずき(原文つき)

この節では、400まわりで実際に出た文言と、そこから読み取る原因・直し方を一覧で確認できます。

出た文言(原文) 出た場所 原因 直し方
400 Request Header Or Cookie Too Large nginx の応答 ヘッダー1行が 8KB 超 Cookie を消す/設計を見直す/buffers を広げる
client sent too long header line nginx error.log 同上 どのヘッダーかが文言に出る
431 Request Header Fields Too Large Node.js の応答 ヘッダー合計 16KB 超 Cookie を減らす。Node の --max-http-header-size
414 Request-URI Too Large nginx の応答 リクエスト行が 8KB 超 長いパラメーターは POST の本文へ
client sent invalid request while reading client request line nginx error.log 生のスペース・壊れた %エンコード URL をエンコードする(encodeURIComponent)
client sent HTTP/1.1 request without "Host" header nginx error.log 自作クライアントが Host を送っていない Host ヘッダーを付ける
client sent invalid method ... "\x16\x03\x01..." nginx error.log HTTP のポートに HTTPS で接続 URL のスキームとポートを合わせる
Expected double-quoted property name in JSON at position 39 Express(body-parser) JSON の末尾にカンマ JSON.stringify で作る
Unexpected token '"', ""hello"" is not valid JSON Express(strict) オブジェクト・配列以外を送った オブジェクトで包む
Failed to decode param '%E3%81%82%E' Express(直接) 壊れた %エンコード URL を作り直す
unsupported charset "SHIFT_JIS" Express(415) charset が UTF-8 以外 UTF-8 で送る
"type":"json_invalid","msg":"JSON decode error" FastAPI(既定 422) JSON の構文が壊れている 送信側を直す。400にするならハンドラー
"type":"missing","msg":"Field required" FastAPI(422) 必須項目が無い loc の項目を送る
Input should be a valid dictionary or object to extract fields from FastAPI(422) Content-Type がフォーム形式・text/plain Content-Type: application/json を付ける

よくある質問

Q. 400 Bad Request とは何ですか? A. サーバーが「リクエストの形がおかしい(クライアント側の誤り)」と判断して処理しなかったことを示すステータスコードです。構文の誤り・大きすぎるヘッダー・壊れた JSON などが原因で、サーバーの故障ではありません。

Q. 400エラーの直し方は(見ている人)? A. URL を確かめる、再読み込みする、そのサイトの Cookie を削除する、シークレットウィンドウか別のブラウザで開く、拡張機能を切る、の順に試します。手元では、Cookie を消した直後に同じ URL が200で開きました。それでも出るならサイトの管理者に、日時・URL・操作を添えて知らせます。

Q. スマホ(iPhone・Android)で400が出ます。 A. パソコンと同じで、原因の多くは Cookie です。ブラウザの設定からそのサイトの Cookie(サイトデータ)を削除するか、別のブラウザで開きます。Wi-Fi とモバイル回線を切り替えて変わるなら、回線側の中継が原因のこともあります。

Q. 「Request Header Or Cookie Too Large」とは何ですか? A. nginx が、Cookie を含むヘッダー1行が上限(既定 8KB)を超えたときに返す400の文言です。手元の実測では、Cookie: と改行を含めて 8,192 バイトが境目でした。見る人は Cookie を消し、作る人は Cookie を小さくするか large_client_header_buffers を広げます。

Q. 400と422はどちらを返すべきですか? A. 構文として読めなければ400、読めたが内容が決まりに反するなら422です。すべて400にまとめる設計も広く使われているので、プロジェクトで揃えて仕様書に書きます。FastAPI は既定でどちらも422、Express の body-parser は構文の失敗を400にします。

Q. 400と414・413の違いは何ですか? A. URL(リクエスト行)が長すぎると414、本文が大きすぎると413、ヘッダー1行が長すぎると nginx は400(Node.js は431)です。手元では、同じ 9,000文字を URL に付けると414、Cookie に付けると400になりました。

Q. ローカルでは動くのに本番だけ400になります。 A. 本番にだけある手前の層(nginx・ロードバランサー・CDN)の上限に当たっていることが多いです。curl -v で Server: ヘッダーと本文を見て誰が返したかを確かめ、アプリのログに該当のリクエストが無ければ手前の層の設定を見ます。

Q. 400の本文には何を書けばよいですか? A. RFC 9457 の Problem Details の形で、title(人向けの短い説明)・status・項目ごとの errors・リクエスト ID を返します。スタックトレースや内部の情報は書きません。

Q. 400はSEO(検索順位)に影響しますか? A. Google 検索セントラル(2026年3月6日更新)は、429 を除く 4xx はすべて同じように扱い、4xx を返す URL はインデックスに登録せず、登録済みの URL は削除される、と説明しています。公開したいページが400になっていないかは、ログアウトした状態や curl で確かめます。

Q. Google のログインで「400. That's an error. Error: invalid_request」と出ます。 A. Google の公式ドキュメントでは、invalid_request は「リクエストの形式が正しくない・必須パラメーターが無い・対応していない認証方法」、redirect_uri_mismatch は「登録したリダイレクト先と一致しない」が原因です。どちらもログインしようとしたアプリ側の設定の問題なので、提供元に知らせます。

Q. curl で JSON を送ると400になります。 A. -d だけでは Content-Type がフォーム形式になります。-H 'Content-Type: application/json' を付け、JSON はシングルクォートで囲んで中はダブルクォートにします。手元では、この指定を付けるだけで415・400が201になりました。

Q. 400を返すとき、ログに本文を残してよいですか? A. 本文にはパスワードや個人情報が混ざるので、丸ごとは残しません。手元では、Content-Type・Content-Length・エラーの種類(err.type)・リクエスト ID を残し、これで原因の切り分けができました。

まとめ

  • 400 Bad Request は「サーバーがリクエストを読めなかった」。直すのは送った側で、再読み込みしても同じ結果になる。
  • 見ている人は、URL の確認→再読み込み→そのサイトの Cookie を削除→別のブラウザ、の順。Cookie を消すと直ることが多い。
  • 手元の nginx では、ヘッダー1行が 8,192 バイトを超えると400(Request Header Or Cookie Too Large)、URL が超えると414、本文が超えると413だった。Node.js はヘッダー超過に431を返した。
  • JSON の構文が壊れていると Express は400、FastAPI は既定で422。構文の失敗は400、読めたが内容が違えば422、形式を受け付けないなら415、と線を引く。
  • 400の本文は Problem Details(どの項目が・なぜ)とリクエスト ID、ログには Content-Type・Content-Length・エラーの種類を残す。「誰が400を返したか」は curl -v の Server ヘッダーとアプリのログの有無で分かる。

次に読むとよい記事です。サーバー側のエラーは関連記事「500エラーとは|Internal Server Errorの原因と直し方を実例で」と関連記事「504 Gateway Time-outの原因と直し方|502・503との違いと実例」で扱っています。

参考・出典

確認日はいずれも 2026年10月29日です。

  • MDN「400 Bad Request」(RFC 9110 15.5.1 を出典とする解説):https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status/400
  • MDN「413 Content Too Large」:https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status/413
  • MDN「414 URI Too Long」:https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status/414
  • MDN「415 Unsupported Media Type」:https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status/415
  • MDN「422 Unprocessable Content」:https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status/422
  • IETF「RFC 9110 HTTP Semantics」15.5 Client Error 4xx:https://httpwg.org/specs/rfc9110.html#status.400
  • IETF「RFC 9457 Problem Details for HTTP APIs」:https://www.rfc-editor.org/rfc/rfc9457
  • nginx「Module ngx_http_core_module」large_client_header_buffers・client_max_body_size:https://nginx.org/en/docs/http/ngx_http_core_module.html
  • Express「express.json([options])」(5.x API):https://expressjs.com/en/5x/api/express/
  • Express「body-parser」Errors:https://expressjs.com/en/resources/middleware/body-parser.html
  • FastAPI「Handling Errors」(RequestValidationError の上書き):https://fastapi.tiangolo.com/tutorial/handling-errors/
  • Google Identity「ウェブサーバー アプリケーションに OAuth 2.0 を使用する」(承認エラー):https://developers.google.com/identity/protocols/oauth2/web-server?hl=ja
  • Google Chrome ヘルプ「Chrome で Cookie を削除、許可、管理する」:https://support.google.com/chrome/answer/95647?hl=ja
  • Google 検索セントラル「HTTP ステータスコード、ネットワーク エラー、DNS エラーが Google 検索に与える影響」:https://developers.google.com/search/docs/crawling-indexing/http-network-errors?hl=ja

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「400 Bad Requestとは|HTTP error 400の原因と直し方・実例」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?