A production error: decide who to call first
The 502 / 503 / 504 comparison tells you whether the app is down, drained or too slow; the Cloudflare 52x cards point straight at “check the origin firewall / certificate / timeout”.
Guide
HTTP Status Codes covers 72 codes: every standard code defined by RFC 9110 (100–101, 200–206, 300–308, 400–418, 421, 422, 426, 500–505), the extension codes defined by RFC 6585 / 7725 / 8297 / 8470 and the WebDAV series (103, 207, 208, 226, 423–425, 428, 429, 431, 451, 506–511), and the non-standard codes that appear in no RFC yet show up every day when you debug production — nginx's 444 and 499, Cloudflare's 520–526.
Updated 2026-09-094 sources9 min read
HTTP Status Codes covers 72 codes: every standard code defined by RFC 9110 (100–101, 200–206, 300–308, 400–418, 421, 422, 426, 500–505), the extension codes defined by RFC 6585 / 7725 / 8297 / 8470 and the WebDAV series (103, 207, 208, 226, 423–425, 428, 429, 431, 451, 506–511), and the non-standard codes that appear in no RFC yet show up every day when you debug production — nginx's 444 and 499, Cloudflare's 520–526.
Unlike tables that give only a “meaning”, every code here is written in four parts: what its semantics are; in what situation you will see it; what the front end / client should do with it (retry? go to login? which response header to read?); and when the back end should return it and which headers to send. High-frequency codes add a fifth part, “common misuse”, because most status-code problems in production come from using the wrong code rather than failing to recognize it — returning 200 for a business failure, 403 for a logged-out user, 503 for rate limiting.
The top of the page also has seven “easily confused” comparisons: 401 vs 403, 301/302/307/308, 400 vs 422, 404 vs 410, 502/503/504, 200/201/202/204, and 429 vs 503. These are the combinations most often asked about in interviews, code reviews and troubleshooting.
502) in the search box and the exact matching card is pinned to the top.Searching “429” gives this card (excerpt):
The matching back-end response looks like this:
429 Too Many Requests Too many requests RFC 6585
Meaning Too many requests were sent within a given time (rate limiting).
When API rate limits, too many login attempts, a crawler being throttled, an AI API over its per-minute quota.
Client Read Retry-After (seconds or a date) and retry with exponential backoff; show a "try later" message; do not retry immediately.
Server Send Retry-After and RateLimit-* headers; count per user / IP / API key.
Misuse Rate limiting returns 403 or 503, so the client cannot tell a permission problem from "wait a while".HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 30
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 30
{"error":"rate_limited","message":"at most 60 requests per minute, please retry in 30 seconds"}The first digit of a status code decides its class, and that is deliberate: even when a client does not know a specific code (a new 425, for instance), it can still handle it by class — every 4xx means “do not retry as-is, the problem is in the request”, every 5xx “retry later”, every 3xx “look at Location”. RFC 9110 explicitly requires clients to understand class semantics and allows them not to understand individual codes. That is also why a custom status code has to fall in the right hundred range.
A historical naming mistake. Early HTTP did not distinguish authentication (who you are) from authorization (what you may do); 401 was named Unauthorized, but its companion WWW-Authenticate header and the semantics of “may succeed after supplying credentials again” both say authentication. RFC 9110 §15.5.2 now words it as "lacks valid authentication credentials". Remember one line: 401 = please log in, 403 = logging in will not help. The front end uses this to decide between redirecting to the login page and showing “no permission”; get it backwards and logged-in users are repeatedly bounced back to the login page.
The four common redirect codes are really a 2×2 matrix. 301 and 308 are permanent (browsers and search engines remember the new address), 302 and 307 are temporary. Historically browsers turned a POST into a GET when handling 301/302; RFC 9110 acknowledges that reality and provides 307/308 as the strict versions that “must keep the method and the request body”. A 301 for an https redirect on a website is fine; when migrating an API endpoint, a POST has to use 307/308 or the request body is lost.
304 is often mistaken for “saving a request”. In reality the browser still sent a conditional request with If-None-Match / If-Modified-Since, the server compared and answered 304 with no response body — what is saved is the transfer, not the round trip. Actually saving requests is the job of Cache-Control: max-age (inside the freshness window the browser does not ask the server at all); the two work together: use the response directly while max-age is valid, and renew it with a conditional request that gets a 304 once it expires. In DevTools, "200 (from disk cache)" is the former and "304" is the latter.
The most common 5xx family in production, and telling them apart narrows the search immediately. A reverse proxy (Nginx, a gateway, a CDN) cannot connect to the upstream, or the upstream returned something invalid — 502, so go and check whether the application process is dead. The upstream explicitly says it is busy or under maintenance — 503, usually returned on purpose, so look at health checks and circuit breakers. The upstream does not answer — 504, so look at slow queries and timeout settings. Cloudflare splits these three cases into 520–526, where 521 ≈ 502 (connection refused), 522/524 ≈ 504 (connection / read timeout) and 525/526 are TLS-layer problems.
The most common anti-pattern is returning 200 for every request and putting the error in a code field of the response body. It lets CDNs cache error pages, disables 5xx alerts in monitoring, makes the browser's fetch().ok meaningless and leaves retry strategies with nothing to work from. The right split is for the HTTP status code to express what happened at the request level (RFC 9110 semantics) and the body's business code to express the domain detail — the two divide the work instead of replacing each other: out of stock is 422 + {"code":"OUT_OF_STOCK"}, not 200 + {"code":500}.
The 502 / 503 / 504 comparison tells you whether the app is down, drained or too slow; the Cloudflare 52x cards point straight at “check the origin firewall / certificate / timeout”.
Write your interceptors from the “front end / client” column: 401 refreshes the token or goes to login, 403 shows “no permission”, 429 reads Retry-After and backs off, 5xx retries a limited number of times and reports.
Does creating a resource return 201 + Location? Does deleting return 204? Is a failed business validation a 400 or a 422? Does rate limiting carry Retry-After? The “back end” and “common misuse” fields are the review checklist.
The seven comparisons cover the most common “what is the difference” questions; the principles section explains the naming of 401, why 307/308 exist and what 304 really does.
The site's entry server (the gateway) cannot connect to the back end that actually handles the request, usually because the back-end process crashed or is restarting. As a user you can only refresh later; if it is your own site, look at the back-end process and the gateway's error log.
Client side: read the Retry-After header, wait the required time before retrying, back off exponentially on repeated failures (1s, 2s, 4s…) and tell the user “too many attempts”. Server side: always send Retry-After with a 429, ideally together with RateLimit-* headers so clients can slow down early.
It depends on the object requested. GET /users/123 where 123 does not exist — 404; GET /users?name=xxx with an empty result — 200 + an empty array. An empty result from a list endpoint is a legitimate “there are results, and the result is empty”, not an error.
204 has no response body, and json() throws a SyntaxError when it parses an empty string. Check response.status === 204 first (or response.headers.get('content-length') === '0') before deciding whether to parse.
They are internal codes nginx writes into its access log: 444 means nginx closed the connection without sending any response, 499 means the client disconnected before nginx answered. In the browser you see “connection reset” or no response at all.
Status-code data is downloaded statically with the page; searching, filtering, copying and favorites all happen locally in your browser and send no requests. Favorites are stored in this browser's localStorage. The external links point to the RFC Editor, IANA and Cloudflare documentation and are handled by those sites once clicked.
Updated 2026-09-09
72 standard, extended, nginx, and Cloudflare HTTP status codes with semantics, common scenarios, frontend/backend handling, misuse notes, and comparisons
The request was received and processing continues; browsers almost never expose this to page scripts.
The request headers have been received; the client may keep sending the request body.
RFC 9110 §15.2.1
The server agrees to switch protocols via the Upgrade header.
RFC 9110 §15.2.2
The server received the request and is processing it, but no final response is ready yet.
RFC 2518 (WebDAV, deprecated)
Some headers are sent before the final response — mostly Link: preload — so the browser can load resources early.
RFC 8297
The request was received, understood and processed successfully.
The request succeeded; the body carries the requested resource or the result of the operation.
RFC 9110 §15.3.1
The request succeeded and a new resource was created.
RFC 9110 §15.3.2
The request was accepted but not processed yet — a receipt for an asynchronous job.
RFC 9110 §15.3.3
The response is successful but its content was modified by an intermediary proxy.
RFC 9110 §15.3.4
Succeeded, but there is no response body.
RFC 9110 §15.3.5
Succeeded, and the client is asked to reset the document view (for example clear a form).
RFC 9110 §15.3.6
Only part of the resource is returned (a successful range request).
RFC 9110 §15.3.7
The body is XML and each sub-resource reports its own status.
RFC 4918 (WebDAV)
Inside a 207 response, the same resource was already reported earlier and is not repeated.
RFC 5842 (WebDAV)
The server applied instance manipulations (delta encoding) to the current instance and returned the difference.
RFC 3229
The client must take further action — usually requesting another address — before the request can complete.
The resource has multiple representations; the client should pick one.
RFC 9110 §15.4.1
The resource has permanently moved to the new URL in Location; use that URL from now on.
RFC 9110 §15.4.2
The resource is temporarily elsewhere; request the original URL again next time.
RFC 9110 §15.4.3
Fetch the result from Location with GET, regardless of the method used for this request.
RFC 9110 §15.4.4
The resource has not changed — use the copy in your cache.
RFC 9110 §15.4.5
The resource must be accessed through the proxy named in Location.
RFC 9110 §15.4.6 (deprecated)
Once meant to switch proxies in a draft; the code is reserved and unused.
RFC 9110 §15.4.7
A temporary redirect, and the client must resend with the same method and body.
RFC 9110 §15.4.8
A permanent redirect that also preserves method and body (the strict version of 301).
RFC 9110 §15.4.9
The request itself has a problem: syntax, authentication, a missing resource, an excessive rate… retrying the same request will not succeed.
The server cannot understand the request: bad syntax, missing or malformed parameters, or JSON that fails to parse.
RFC 9110 §15.5.1
Credentials are missing or invalid — "who are you?" (the name says Unauthorized, but it really means unauthenticated).
RFC 9110 §15.5.2
Reserved for future digital payments; the spec defines no concrete semantics.
RFC 9110 §15.5.3 (reserved)
The server knows who you are, but you lack permission — "you may not". Re-authenticating will not help.
RFC 9110 §15.5.4
The server cannot find the requested resource and does not say whether that is temporary or permanent.
RFC 9110 §15.5.5
The resource exists but does not support this HTTP method.
RFC 9110 §15.5.6
The server has no representation that satisfies the Accept / Accept-Language requirements.
RFC 9110 §15.5.7
Authentication against the proxy server is required first (the proxy version of 401).
RFC 9110 §15.5.8
The server waited too long for the request (the client never finished sending it).
RFC 9110 §15.5.9
The request conflicts with the current state of the resource; the user may resolve it by changing the request.
RFC 9110 §15.5.10
The resource used to exist and is now permanently gone, with no forwarding address.
RFC 9110 §15.5.11
The server requires the request to carry Content-Length.
RFC 9110 §15.5.12
A condition in the request headers (If-Match, If-Unmodified-Since) was not met.
RFC 9110 §15.5.13
The request body exceeds the size the server allows.
RFC 9110 §15.5.14 (formerly Payload Too Large)
The URL is too long for the server to process.
RFC 9110 §15.5.15
The server does not support the format (Content-Type) of the request body.
RFC 9110 §15.5.16
The byte range requested in the Range header is beyond the resource size.
RFC 9110 §15.5.17
The server cannot satisfy the Expect header.
RFC 9110 §15.5.18
A joke from a 1998 April Fools' RFC; it should not be used in real HTTP.
RFC 2324 (April Fools' RFC); marked unused by RFC 9110
The request reached a server that cannot serve responses for that authority (domain).
RFC 9110 §15.5.20
The request syntax and format are fine, but it cannot be processed semantically (business validation failed).
RFC 9110 §15.5.21 (formerly Unprocessable Entity)
The resource is locked.
RFC 4918 (WebDAV)
This operation failed because a dependent operation failed first.
RFC 4918 (WebDAV)
The server is unwilling to process a request that could be replayed (TLS 1.3 0-RTT early data).
RFC 8470
The server refuses to handle the request on the current protocol and asks the client to switch to the one named in Upgrade.
RFC 9110 §15.5.22
The server requires a conditional header (such as If-Match) to prevent lost updates.
RFC 6585
Too many requests were sent in a given time window (rate limited).
RFC 6585
A request header (one field or the total) is too large.
RFC 6585
The resource is withheld for legal reasons (copyright, government orders). The number nods to the novel Fahrenheit 451.
RFC 7725
The server knows it failed or cannot fulfil a valid request; retrying later may succeed.
The server hit an unexpected condition and could not complete the request.
RFC 9110 §15.6.1
The server does not support the functionality needed to complete the request (usually an unknown method).
RFC 9110 §15.6.2
A server acting as a gateway or proxy received an invalid response from its upstream.
RFC 9110 §15.6.3
The server is temporarily unable to handle the request (overloaded or under maintenance), usually only for a while.
RFC 9110 §15.6.4
The gateway/proxy timed out waiting for an upstream response.
RFC 9110 §15.6.5
The server does not support the HTTP major version used by the request.
RFC 9110 §15.6.6
A content negotiation misconfiguration created a loop.
RFC 2295
The server cannot store the content needed to complete the request.
RFC 4918 (WebDAV)
An infinite loop was detected while processing the request.
RFC 5842 (WebDAV)
The request must be extended further before the server can process it.
RFC 2774 (historical)
The client must authenticate at the network level (captive portal) before getting online.
RFC 6585
They are in no RFC and only appear in particular server logs or CDN error pages; knowing them saves a lot of debugging time.
nginx closes the connection without sending any response; it only appears in nginx logs.
nginx-specific
The client disconnected before the server responded; it only appears in nginx logs.
nginx-specific
The response the origin sent to Cloudflare was empty, malformed or had oversized headers.
Cloudflare-specific
Cloudflare cannot reach the origin (the TCP connection was refused).
Cloudflare-specific
The TCP connection from Cloudflare to the origin timed out.
Cloudflare-specific
Cloudflare cannot route to the origin (DNS or network routing problem).
Cloudflare-specific
TCP connected, but the origin did not return an HTTP response within 100 seconds.
Cloudflare-specific
The TLS handshake between Cloudflare and the origin failed.
Cloudflare-specific
In Full (strict) mode the origin certificate is expired, self-signed or does not match the domain.
Cloudflare-specific