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
请求已收到,继续处理;浏览器几乎不会把它暴露给页面脚本。
请求头已收到,客户端可以继续发送请求体。
RFC 9110 §15.2.1
服务器同意按 Upgrade 头切换到另一个协议。
RFC 9110 §15.2.2
服务器已收到并在处理请求,但尚无最终响应。
RFC 2518(WebDAV,已废弃)
在最终响应之前先发一部分头(主要是 Link: preload),让浏览器提前加载资源。
RFC 8297
请求被成功接收、理解并处理。
请求成功,响应体里是请求的资源或操作结果。
RFC 9110 §15.3.1
请求成功且创建了新资源。
RFC 9110 §15.3.2
请求已接受但尚未处理完,是异步任务的回执。
RFC 9110 §15.3.3
响应成功,但内容被中间代理修改过。
RFC 9110 §15.3.4
成功,但没有响应体。
RFC 9110 §15.3.5
成功,且要求客户端重置「文档视图」(如清空表单)。
RFC 9110 §15.3.6
返回的是资源的一部分(Range 请求成功)。
RFC 9110 §15.3.7
响应体是 XML,里面每个子资源各有自己的状态码。
RFC 4918(WebDAV)
207 响应里,同一个资源已在前面报告过,不再重复。
RFC 5842(WebDAV)
服务器对当前实例应用了「实例操作」(增量编码)返回差异。
RFC 3229
需要客户端进一步动作(通常是换个地址再请求)才能完成。
资源有多个表示,请客户端选一个。
RFC 9110 §15.4.1
资源永久搬到 Location 指向的新地址,以后都用新地址。
RFC 9110 §15.4.2
资源暂时在别处,下次仍请求原地址。
RFC 9110 §15.4.3
请用 GET 去 Location 看结果(与当前请求方法无关)。
RFC 9110 §15.4.4
资源没变,用你缓存里的那份。
RFC 9110 §15.4.5
必须通过 Location 指定的代理访问。
RFC 9110 §15.4.6(已弃用)
曾在草案中表示「切换代理」,现已保留不用。
RFC 9110 §15.4.7
临时跳转,且客户端必须用**同样的方法和请求体**重发。
RFC 9110 §15.4.8
永久跳转,且保持方法与请求体(301 的严格版)。
RFC 9110 §15.4.9
请求本身有问题:语法、鉴权、资源不存在、频率过高……重试同样的请求不会成功。
服务器无法理解请求:语法错、参数缺失/格式错、JSON 解析失败。
RFC 9110 §15.5.1
缺少或无效的身份凭证——「你是谁?」(名字叫 Unauthorized,实际含义是 Unauthenticated)。
RFC 9110 §15.5.2
为将来的数字支付保留,规范未定义具体语义。
RFC 9110 §15.5.3(保留)
服务器知道你是谁,但你没有权限——「你不能」。重新认证也没用。
RFC 9110 §15.5.4
服务器找不到请求的资源,且不说明是临时还是永久。
RFC 9110 §15.5.5
资源存在,但不支持这个 HTTP 方法。
RFC 9110 §15.5.6
服务器没有能满足 Accept / Accept-Language 等要求的表示。
RFC 9110 §15.5.7
需要先向代理服务器认证(401 的代理版)。
RFC 9110 §15.5.8
服务器等请求等太久(客户端迟迟没发完)。
RFC 9110 §15.5.9
请求与资源当前状态冲突,用户可能通过修改请求解决。
RFC 9110 §15.5.10
资源曾经存在,现在永久移除,且没有转发地址。
RFC 9110 §15.5.11
服务器要求请求带 Content-Length。
RFC 9110 §15.5.12
请求头里的条件(If-Match、If-Unmodified-Since)不满足。
RFC 9110 §15.5.13
请求体超过服务器允许的大小。
RFC 9110 §15.5.14(旧名 Payload Too Large)
URL 太长服务器拒绝处理。
RFC 9110 §15.5.15
请求体的格式(Content-Type)服务器不支持。
RFC 9110 §15.5.16
Range 头请求的字节范围超出资源大小。
RFC 9110 §15.5.17
无法满足 Expect 头的要求。
RFC 9110 §15.5.18
1998 年愚人节 RFC 的玩笑,不应在真实 HTTP 里使用。
RFC 2324(愚人节 RFC);RFC 9110 标记为未使用
请求发到了无法为该 authority(域名)提供响应的服务器。
RFC 9110 §15.5.20
请求语法正确、格式也对,但语义上无法处理(业务校验失败)。
RFC 9110 §15.5.21(旧名 Unprocessable Entity)
资源被锁定。
RFC 4918(WebDAV)
因为前一个依赖操作失败,本操作也失败。
RFC 4918(WebDAV)
服务器不愿处理可能被重放的请求(TLS 1.3 0-RTT 早期数据)。
RFC 8470
服务器拒绝在当前协议上处理,要求切换到 Upgrade 头指定的协议。
RFC 9110 §15.5.22
服务器要求请求必须带条件头(如 If-Match),防止「丢失更新」。
RFC 6585
在给定时间内发了太多请求(限流)。
RFC 6585
请求头(总和或单个)太大。
RFC 6585
因法律要求(版权、政府命令)拒绝提供资源。编号致敬《华氏 451》。
RFC 7725
服务器知道自己出错或无法完成合法请求;稍后重试可能成功。
服务器遇到了意料之外的情况,无法完成请求。
RFC 9110 §15.6.1
服务器不支持完成请求所需的功能(通常是不认识的方法)。
RFC 9110 §15.6.2
作为网关/代理的服务器从上游收到了无效响应。
RFC 9110 §15.6.3
服务器暂时无法处理(过载或维护),通常是临时的。
RFC 9110 §15.6.4
网关/代理等上游响应超时。
RFC 9110 §15.6.5
服务器不支持请求使用的 HTTP 主版本。
RFC 9110 §15.6.6
内容协商配置错误导致循环。
RFC 2295
服务器无法存储完成请求所需的内容。
RFC 4918(WebDAV)
处理请求时检测到无限循环。
RFC 5842(WebDAV)
请求需要进一步扩展才能被处理。
RFC 2774(历史)
客户端需要先通过网络层认证(强制门户)才能上网。
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 直接关闭连接、不发任何响应;只出现在 Nginx 日志里。
nginx 私有
服务器还没响应,客户端就断开了;只出现在 Nginx 日志。
nginx 私有
源站给 Cloudflare 的响应是空的、格式不对或超出头大小限制。
Cloudflare 私有
Cloudflare 连不上源站(TCP 连接被拒)。
Cloudflare 私有
Cloudflare 向源站发起 TCP 连接超时。
Cloudflare 私有
Cloudflare 无法路由到源站(DNS 或网络路由问题)。
Cloudflare 私有
TCP 已连上,但源站在 100 秒内没有返回 HTTP 响应。
Cloudflare 私有
Cloudflare 与源站的 TLS 握手失败。
Cloudflare 私有
Full (strict) 模式下源站证书过期、自签或域名不匹配。
Cloudflare 私有