ByteScope

400

How to fix HTTP 400 Bad Request

4xx client errorIETF standardRFC 9110

The server refused to interpret the request at all — something in the request line, a header, the framing or the body is malformed or oversized — which usually means none of your application code ever ran.

Why you are seeing 400, and what to do about it

The same three digits are three different problems depending on which side of the request you are on. Read the block that describes you.

Reproduce 400 with curl

Run this against the URL that failed. It prints the status without the error page, so you can see what the server said rather than what the browser rendered.

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

This sends one enormous `Cookie` header — 20 000 bytes, comfortably past the 8k that nginx's `large_client_header_buffers` allows by default — so against anything running nginx's defaults it reproduces the most common 400 on the public web deliberately rather than waiting for it. `-D -` prints the response headers, `-o /dev/null` discards the error page, and the status line is the result. Three readings follow. Drop the `-H` and re-run: a 200 proves the request shape is fine and only the size was wrong, which is a buffer question and not a syntax one. Halve the byte count until the 400 turns into a 200 and you have measured the server's actual header limit, which is the number to compare against `large_client_header_buffers`. If that first run comes back 200 instead, nothing is wrong with the command — the limit in front of you is simply larger than what you sent, which several edges are: double the padding to 40 000 and keep doubling until the status flips, because every reading here assumes you have actually seen the 400. And read `server:` and any `cf-ray` in the dump to learn who refused it, because an edge and an origin can have different limits and only one of them is in your config. For the other big family, run `curl -v http://example.com:443/`, which sends plaintext HTTP to the TLS port: nginx answers that with a 400 whose body reads "The plain HTTP request was sent to HTTPS port", and recognising that sentence saves an hour of reading application logs that contain nothing.

What 400 actually means

RFC 9110 §15.5.1 defines 400 as the server not processing the request due to something perceived to be a client error, and names three examples: malformed request syntax, invalid request message framing, and deceptive request routing. That single sentence covers two failures that live in different buildings, and telling them apart is most of the work. A protocol-level 400 is written by the server or the edge in front of it before any handler is dispatched — RFC 9110 §7.2 requires a server to answer 400 to any HTTP/1.1 request that lacks a `Host` field, carries more than one, or carries an invalid one, and RFC 9112 §6.3 makes a message carrying both `Content-Length` and `Transfer-Encoding` an error tied to request smuggling, which hardened servers reject rather than guess at. An application-level 400 is your own handler rejecting a body it successfully parsed. The first leaves no line in your application log; the second is a line in it, and that asymmetry is the fastest test available. RFC 9110 also offers a sharper code for the second case: §15.5.21 defines 422 (Unprocessable Content) for content whose syntax is correct but whose instructions cannot be followed, which is what most "validation failed" responses actually are.

Codes 400 gets mistaken for

Confused withHow to tell them apart
422400 is for a request the server could not interpret; 422 (Unprocessable Content, RFC 9110 §15.5.21) is for content it interpreted perfectly and still cannot act on. A JSON body that fails your schema parsed fine — it is semantics, not syntax — so 422 tells the client the payload arrived intact and the values are wrong, which is a different fix from "your request was malformed".
414Both are size limits and they sit either side of the first line break. nginx documents that a request *line* longer than the buffer returns 414 (URI Too Long, §15.5.15), while a header *field line* that is too long returns 400. So a 400 points at cookies and headers, and a 414 points at a query string somebody built in a loop — the code alone tells you which half of the request to shrink.
431431 (Request Header Fields Too Large, RFC 6585 §5) is the code that exists precisely for the oversized-cookie case, and it is far more informative than 400. nginx does not use it — it answers 400 with a "Request Header Or Cookie Too Large" body — so the absence of a 431 on the public web says nothing about whether headers are the problem.
nginxnginx writes the actual reason to its error log before it writes 400 to the client, and the messages are specific where the status code is not: `client sent too long header line` is size, `client sent invalid method while reading client request line` is syntax, and internal code 497 — plaintext sent to a TLS port — surfaces to the client as a 400 with an unmistakable body. One log line replaces an afternoon of guessing.
Reason phrase
Bad Request
Class
4xx client error
Defined by
RFC 9110
Standard
IETF standard

Tools on this site

All of these run in your browser — nothing is uploaded.

Related status codes

Codes people usually end up reading in the same session as 400.

Frequently asked questions

How do I find out which server produced this status code?

Read the response headers rather than the page. curl -sS -o /dev/null -D - https://example.com/path prints them without the body, and server, via and cf-ray between them name the layer that answered. A cf-ray value means Cloudflare handled the response and is the id their support will ask for. To take the edge out of the picture entirely, repeat the request with --resolve example.com:443:203.0.113.10, which connects to the origin address you name while still sending the original host name and SNI — if the answer changes, the edge and the origin disagree.

Does the reason phrase after the number matter?

No, and you should never branch on it. RFC 9112 tells clients to ignore the reason phrase, servers are free to change it, and HTTP/2 and HTTP/3 do not carry one at all — so 404 Not Found over HTTP/1.1 arrives as a bare 404 over HTTP/2. The phrases on this site are the registered ones because they are what people search for and what appears in an HTTP/1.1 log, not because any software depends on them.

Should I retry a request that failed with this code?

Retry 5xx and 429; do not retry 4xx, because nothing about the request will be different next time. If the response carries Retry-After, honour it — RFC 9110 defines it for exactly this, and it may be either a number of seconds or a date. Otherwise use exponential backoff with jitter and a hard cap, and only for idempotent methods: a retried POST can charge a card twice. A retry storm against a server that is already failing is how a brief incident becomes a long one.

Can I make my server return a different status code?

Yes, and the only rule that matters is that the code has to be true. Anything that reads your responses automatically — search engines, monitoring, caches, client retry logic — makes decisions from the number and never from the page. Serving an error page with 200 hides the failure from your own alerting; serving 200 for a missing page gets the error indexed as content; returning 500 for a request that was simply malformed sends whoever is on call to the wrong half of the stack.

Still stuck on 400? browse every status code in the reference — or go back up to the block written for your side of the stack.