HTTP status code · Client errors (4xx)
400 Bad Request
400 Bad Request means the server received the request but refuses to process it because something in it looks wrong: broken syntax, invalid framing or data it cannot parse. In browsers the usual culprit is a corrupted or oversized cookie; in APIs it is a body that is not valid JSON or fails validation.
| Class | 4xx, Client errors |
|---|---|
| Defined in | RFC 9110 §15.5.1 |
| Cacheable by default | Only with explicit Cache-Control or Expires |
| Safe to retry | Only after changing the request; resending it unchanged fails again |
| Relevant headers |
|
What 400 means
RFC 9110, section 15.5.1, keeps 400 deliberately broad: the server "cannot or will not process the request due to something that is perceived to be a client error", with malformed syntax, invalid message framing and deceptive request routing as examples. It is the generic fallback of the 4xx class, so any client that meets an unknown 4xx code treats it as a 400.
Because it is so generic, the status alone rarely tells you what is wrong. Servers are expected to explain in the response body. nginx, for example, sends 400 with the text "Request Header Or Cookie Too Large" when headers exceed large_client_header_buffers, and "The plain HTTP request was sent to HTTPS port" when someone speaks plain HTTP to a TLS listener.
For APIs there is a long-running split between 400 and 422 for validation errors. A common convention: 400 when the body cannot be parsed at all (invalid JSON, wrong Content-Type), 422 when it parses but the values break business rules. FastAPI answers 422 for validation failures by default, while Express with express.json() answers 400 for unparseable JSON.
Common causes
If you are visiting the site
- Cookies for the site grew too large or got corrupted, so the request headers exceed what the server accepts. This is the classic "400 Bad Request: Request Header Or Cookie Too Large" page.
- A mistyped or badly encoded URL, such as a stray % that is not followed by two hex digits, or characters pasted from a document.
- A browser extension or an outdated cached page submitting a form with fields the server no longer expects.
If you run the server
- The client sent a body that is not valid JSON (a trailing comma, single quotes) or sent JSON with Content-Type: text/plain, so the parser fails.
- Required query parameters or fields are missing, or have the wrong type, and the handler rejects them.
- An HTTP/1.1 request without a Host header, which RFC 9112 requires servers to reject with 400.
- Plain HTTP sent to the HTTPS port, or a proxy forwarding a request whose headers it rewrote incorrectly.
- Header or cookie size above the server limit: nginx large_client_header_buffers, Node.js --max-http-header-size (16 KB by default).
How to fix it
If you are visiting the site
- Clear the cookies for that one site (in Chrome: the site information icon in the address bar, then Cookies and site data) and reload.
- Retype the URL by hand instead of pasting it, and remove any odd characters or trailing symbols.
- Try a private window; if it works there, an extension or stale cache is the cause.
If you run the server
- Return a body that names the problem, such as the field and the rule it broke, so clients can fix it without guessing.
- Validate the request before touching business logic, and answer 400 for unparseable input instead of letting the parser throw a 500.
- If users hit cookie-size 400s, find what keeps adding cookies (analytics, A/B testing, oversized session data) or raise large_client_header_buffers in nginx as a stopgap.
- Reproduce with curl -v and the exact headers and body; the verbose output shows whether the problem is the Host header, the encoding or the payload.
How to send 400
app.post('/orders', (req, res) => {
res.status(400).json({ error: 'quantity must be a positive integer', field: 'quantity' });
});// app/orders/route.ts
export async function POST() {
return Response.json(
{ error: 'quantity must be a positive integer', field: 'quantity' },
{ status: 400 }
);
}mux.HandleFunc("POST /orders", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest) // 400
w.Write([]byte(`{"error":"quantity must be a positive integer","field":"quantity"}`))
})from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
@app.post("/orders")
def create_order():
return JSONResponse(status_code=400, content={"error": "quantity must be a positive integer", "field": "quantity"})Commonly confused with
- 400 vs 422
- 422 means the body was understood but its values are invalid; many APIs keep 400 for input that cannot be parsed at all.
- 400 vs 404
- 404 means the request was well formed but nothing exists at the URL; 400 means the request itself is broken.
- 400 vs 431
- 431 is the specific code for headers that are too large; nginx still answers 400 for oversized cookies and headers.
Frequently asked questions
- How do I fix "400 Bad Request: Request Header Or Cookie Too Large"?
- Delete the cookies for that site and reload. The server, usually nginx, rejects requests whose headers exceed its buffer, and accumulated cookies are the usual reason. Site owners can raise large_client_header_buffers, but the real fix is to stop setting so many cookies.
- Is a 400 error my fault or the website's?
- The server is saying the request was wrong, so something on the sending side has to change. For a regular visitor that usually means stale cookies or a bad URL. For a site whose own forms produce 400s, the bug is in the site.
- Should an API return 400 or 422 for validation errors?
- Both are defensible. RFC 9110 defines 422 for content that is syntactically correct but semantically invalid, so many APIs use 400 for unparseable input and 422 for rule violations. Pick one convention and document it.
- Can a 400 error be temporary?
- Rarely. Resending the same request gives the same answer. It only looks temporary when something changes in between, such as cookies expiring or a deploy relaxing a validation rule.
Last reviewed by Arielton Oberek.