Guides

7 min readUpdated

400 Bad Request error: what causes it and how to fix it

Quick answer

A 400 Bad Request error means a server rejected the request because something in it looked wrong: a malformed URL or header, an oversized or corrupt cookie, broken message framing, or plain HTTP sent to an HTTPS port. Any layer can send it, from the CDN to the reverse proxy to the application, and each words its page differently. Visitors can often fix it by clearing the site's cookies; owners should find which layer answered and read its log.

01What does error 400 Bad Request mean?

RFC 9110 defines 400 as the status a server sends when it "cannot or will not process the request due to something that is perceived to be a client error", and gives three examples: malformed request syntax, invalid message framing and deceptive request routing. It is the general-purpose 4xx code, and clients treat any 4xx they do not recognise as a 400.

Two consequences follow. Sending the same request again will fail again, because nothing about it has changed. And the code says little by itself: a 400 from a CDN about a broken URL and a 400 from an API about a missing JSON field have nothing in common except the number. The HTTP status code reference shows how 400 relates to the more specific codes such as 413, 414, 422 and 431.

02Causes of a 400, sorted by the layer that rejects them

Start by asking which layer refused the request, because each one checks different things.

LayerTypical causesWhat you usually see
Browser or clientCorrupt or oversized cookies, a hand-edited URL with a stray % or space, an extension adding headers, a broken script building requestsWorks in a private window or another browser
CDN or WAF edgeInvalid percent-encoding, header size limits, protocol violations, a custom rule that answers 400The provider's branded page or Server header
Reverse proxy or web serverRequest header or cookie too large, missing or duplicate Host, invalid header line, Content-Length together with Transfer-Encoding, HTTP sent to the HTTPS portA plain server page, such as nginx's
Application or APIInvalid JSON, missing or malformed parameters, OAuth invalid_requestA JSON or HTML error from the app itself

03What the 400 page says on each server

The wording of the page is the quickest clue. The nginx texts below are its built-in pages, reproduced on nginx 1.22.1; the Node.js response was reproduced on Node.js 22, the Cloudflare page was observed, and the others are quoted from each project's source code or documentation.

ServerMessage on the pageWhat it means
nginx400 Bad Request, Request Header Or Cookie Too LargeOne header line exceeded one large_client_header_buffers buffer (8 KB by default), or the headers exceeded all four together
nginx400 Bad Request, The plain HTTP request was sent to HTTPS porthttp:// was sent to a TLS port, often a proxy or load balancer pointed at port 443 without TLS
nginx400 Bad Request, No required SSL certificate was sentThe server requires a client certificate (mutual TLS) and none was presented
nginx400 Bad Request, with nothing elseAnything else; the reason is only in the error log at info level
Apache httpdYour browser sent a request that this server could not understand. Size of a request header field exceeds server limit.A header exceeded LimitRequestFieldSize (8,190 bytes by default)
Apache httpdThe number of request header fields exceeds this server's limit.More than LimitRequestFields (100 by default)
IIS (HTTP.sys)HTTP 400 - Bad Request (Request header too long)A header passed MaxFieldLength or MaxRequestBytes; the reason is logged in the HTTPERR log
Node.jsAn empty body: just HTTP/1.1 400 Bad Request and Connection: closeThe HTTP parser rejected the request (oversized headers get 431 instead)
Go net/http400 Bad Request: missing required Host header (or malformed Host header, invalid header name)The reason is part of the response text
CloudflareAn nginx-style 400 page with cloudflare in the footer and Server: cloudflare (observed October 2026)Rejected at the edge before reaching the origin

04The curl test that shows which layer rejected the request

Two comparisons answer most questions. First, send the request through the public hostname and straight to the origin with the same hostname and TLS server name: if only the first returns 400, the edge or a proxy rejected it, and the Server header says which. Second, if your browser gets a 400 and curl does not, the difference lies in what the browser sends.

For the second case, open the browser's developer tools, find the failing request in the Network panel, right-click it and choose Copy, then Copy as cURL. Paste the command into a terminal, confirm that it still fails, and delete headers one at a time, starting with Cookie, until it succeeds. The last header you removed is the cause.

Edge or origin? (replace example.com and 203.0.113.10)
# through the CDN or proxy
curl -s -o /dev/null -D - 'https://example.com/failing/path' | grep -iE '^(HTTP|server|via|cf-ray|x-cache)'

# straight to the origin, same Host header and SNI
curl -s -o /dev/null -D - --resolve example.com:443:203.0.113.10 'https://example.com/failing/path' | grep -iE '^(HTTP|server)'

# how many bytes the request itself was
curl -s -o /dev/null -w '%{http_code} %{size_request} bytes\n' 'https://example.com/failing/path'

05Why nginx logs nothing for a 400, and how to see the reason

nginx writes the cause of a client-side 400 at the info level, and the error_log default level is error, so on a stock configuration the access log shows the 400 and the error log stays silent. Raise the level for a short while, reproduce the failure, then set it back, because info is verbose on a busy server.

Every line below was produced on nginx 1.22.1 by sending the matching broken request; long values are shortened.

Error log line containsCause
client sent too long header line: "Cookie: ..."A cookie or other header too large for the buffers
client sent too long URIThe request line exceeded one buffer; nginx answers 414, not 400
client sent plain HTTP request to HTTPS porthttp:// sent to a TLS listener
client sent HTTP/1.1 request without "Host" headerA hand-written or broken client
client sent duplicate host headerTwo Host headers, a classic sign of a probe or a broken proxy
client sent invalid header lineA header name containing a space or a control character; names with other invalid characters, such as Bad@Header, are logged with the same text but ignored, not rejected
client sent "Content-Length" and "Transfer-Encoding" headers at the same timeAmbiguous framing, the raw material of request smuggling
client sent invalid requestMalformed percent-encoding such as %zz, or %00, in the URL
Temporarily raise the nginx error log level, then reload
# /etc/nginx/nginx.conf, main context
error_log /var/log/nginx/error.log info;

nginx -t && systemctl reload nginx
tail -f /var/log/nginx/error.log | grep 'client sent'

06How to fix a 400 Bad Request as a visitor

  • Check the address for stray characters: a % that is not followed by two hex digits, spaces, or a URL pasted twice
  • Delete the cookies for that one site and reload; this fixes the cookie-size and corrupt-cookie cases
  • Try a private window: if it works there, stored site data or an extension is the cause
  • Disable browser extensions that rewrite pages or add headers, then retry
  • If an upload fails, try a smaller file; some applications answer an oversized upload with 400 rather than 413

07How to fix a 400 as a site owner

Treat the 400 as a symptom and fix the component that produced the bad request. Raising limits or disabling checks only moves the problem.

  • Oversized cookies: find the cookie that grew before raising nginx's large_client_header_buffers or Apache's LimitRequestFieldSize
  • Plain HTTP to the HTTPS port: make the upstream scheme match, for example proxy_pass https:// for a TLS backend, or point the proxy at the HTTP port; the reverse mistake, plain HTTP answering on port 443, shows up in Chrome as ERR_SSL_PROTOCOL_ERROR
  • Missing or wrong Host from a proxy or custom client: send the hostname the backend expects, for example proxy_set_header Host $host in nginx
  • Ambiguous Content-Length and Transfer-Encoding: fix the client or library that sends both; do not relax the check, which protects against request smuggling
  • API validation errors: return a body that names the field and the problem, for example in the Problem Details format of RFC 9457, so callers can fix their request
  • A sudden spike in 400s from many addresses often means scanning or a flood of junk requests rather than a bug; a WAF absorbs that traffic before it reaches the origin

Questions

Frequently asked questions

Either is possible. A 400 says the server considered the request malformed. Sometimes the request really is wrong, for example a mistyped address or a corrupt cookie in your browser, and clearing the site's cookies fixes it. Sometimes the site set cookies that grew too large, or a proxy in its chain is misconfigured, and only the operator can fix that. If a private window works, the cause is on your side.

Your browser sends every cookie for the site with each request. When those cookies grow past the server's header limit, or one becomes corrupt, the server rejects the whole request with 400 before the application sees it. Deleting the cookies for that site makes the next request small and clean again. You will be signed out, and the error can return if the site keeps adding cookies.

400 Bad Request means the server could not or would not parse the request, for example invalid JSON or broken HTTP framing. 422 Unprocessable Content means the request was well-formed but its content failed validation, such as an email address without a domain. Many APIs use 400 for both; using 422 for validation failures lets clients tell a broken request from a rejected value.

Because the browsers send different requests. Each keeps its own cookies and extensions, and either can push the request over a server limit or add a header the server rejects. The browser that fails is usually carrying old or oversized cookies for that site. Clearing that browser's cookies for the site, or disabling its extensions one by one, usually reveals the cause.

Only if search engine crawlers receive them for real pages. Google treats 4xx codes other than 429 as content that does not exist, so a public page that returns 400 to crawlers can drop out of the index. Errors that only browsers with oversized cookies receive do not affect crawling, because crawlers usually send few or no cookies, but they still cost you those visitors.

Sources

Sources and verification

nginx 1.22.1 error pages and every error log line in the table were reproduced in an isolated network namespace with curl 7.88.1 and raw requests; the Node.js 22 parser responses were reproduced the same way; the Go messages are quoted from the net/http source; the curl commands were run with curl 7.88.1 against a public example host, which also returned the Cloudflare 400 page for a malformed URL. Debian 12, October 2026.

  1. 01RFC 9110, section 15.5.1: 400 Bad Request · RFC Editor
  2. 02Module ngx_http_core_module: large_client_header_buffers · nginx.org
  3. 03nginx source: ngx_http_special_response.c (built-in error pages) · nginx on GitHub
  4. 04Apache httpd source: server/protocol.c (header limit messages) · Apache on GitHub
  5. 05Apache core: LimitRequestFieldSize and LimitRequestFields · Apache HTTP Server
  6. 06HTTP 400 error responses to HTTP requests (Request header too long) · Microsoft Learn
  7. 07Types of errors logged by the HTTP Server API · Microsoft Learn
  8. 08Configuring HTTP Server API error logging · Microsoft Learn
  9. 09Node.js HTTP: Event 'clientError' · Node.js
  10. 10Go source: net/http server.go · Go on GitHub
  11. 11Chrome DevTools Network features reference (Copy as cURL) · Chrome for Developers
  12. 12RFC 9457, Problem Details for HTTP APIs · RFC Editor

Last updated