Free HTTP status code reference
HTTP status codes, and which one to send
This reference is free and needs no signup: all 63 registered HTTP status codes from 100 Continue to 511 Network Authentication Required, each with its name, the RFC section that defines it today, what it asserts, and the neighbouring code people send by mistake. Type a number into the box to jump straight to one, or read the chart — it is plain HTML, so find-on-page works and nothing needs to load first. Beyond the registry it also covers the 16 vendor codes that fill real logs, from nginx 499 to Cloudflare 524.
- 100% free
- No signup
- 63 registered codes
- RFC 9110 sections
- Works with JS off
Type a number and the definition appears here. The full chart below is plain HTML, so it is also searchable with your browser’s own find-on-page and readable with JavaScript off.
The full status code chart
Five classes, 63 codes. The first digit is the only part a client is obliged to understand: a cache that has never heard of 308 still knows it is a redirect, which is what made adding codes to HTTP possible at all. Rows marked cacheable may be stored by a shared cache even when you send no Cache-Control header.
1xx informational
Interim responses. The connection stays open and a final status still follows, which is why most application frameworks never let you send one directly.
| Code | Name | Defined in | What it means | When to send it |
|---|---|---|---|---|
| 100 | Continue | RFC 9110 §15.2.1 | The request headers were accepted; send the body now. | Sent by the server in reply to an Expect: 100-continue header, never invented by application code. It exists so a client can avoid pushing a 2 GB upload at an endpoint that would have rejected the headers anyway. |
| 101 | Switching Protocols | RFC 9110 §15.2.2 | The server is changing to the protocol named in the Upgrade header. | The last HTTP/1.1 status a WebSocket connection ever sees — after it, the bytes on the socket are frames, not HTTP. HTTP/2 has no upgrade dance, so a WebSocket over h2 uses an extended CONNECT and a plain 200 instead. |
| 102 | Processingdormant | RFC 2518 §10.1 | The WebDAV request is still running; do not time out. | A keep-alive ping for long WebDAV operations, deliberately dropped when RFC 4918 replaced RFC 2518 in 2007. It survives in the IANA registry and nowhere else — reach for 202 with a status URL instead. |
| 103 | Early Hints | RFC 8297 | Preliminary Link headers, sent before the real response. | Send it while your origin is still assembling the page so the browser can start fetching stylesheets and fonts named in Link: rel=preload. Chrome, Firefox and Safari all act on it; the win is measured in the hundreds of milliseconds a slow origin spends thinking. |
2xx success
The request was received, understood and accepted. The differences between them are about what the response body contains and what the client should do with its current page.
| Code | Name | Defined in | What it means | When to send it |
|---|---|---|---|---|
| 200 | OKcacheable | RFC 9110 §15.3.1 | The request succeeded and the body is the result. | The default, and the one to stop overusing: an API that answers 200 with {"error": …} inside defeats every retry policy, cache and monitoring dashboard downstream. What the body means depends on the method — for GET it is the resource, for POST it is a description of what happened. |
| 201 | Created | RFC 9110 §15.3.2 | One or more resources now exist that did not before. | Pair it with a Location header pointing at the new resource, which is the half most APIs forget. A PUT that overwrites an existing resource returns 200 or 204; only the call that brought something into being returns 201. |
| 202 | Accepted | RFC 9110 §15.3.3 | The request was queued, and the outcome is not known yet. | The honest answer for anything that hands work to a queue: video encoding, bulk imports, sending mail. Return a body or Location that lets the client poll for the result, because 202 promises nothing about whether the work will ultimately succeed. |
| 203 | Non-Authoritative Informationcacheable | RFC 9110 §15.3.4 | A proxy modified the payload the origin sent. | Emitted by transforming proxies — an image compressor or an annotation gateway — not by your application. Seeing it in a client log means something between you and the origin rewrote the body. |
| 204 | No Contentcacheableno body | RFC 9110 §15.3.5 | It worked and there is deliberately nothing to send back. | The right answer to a DELETE that succeeded or a PUT that changed nothing visible. A 204 must carry no body at all — not an empty JSON object, not a newline — and browsers will not navigate away from the current page when they receive one. |
| 205 | Reset Contentno body | RFC 9110 §15.3.6 | Succeeded; clear the form the user just submitted. | Like 204 but with an instruction attached: reset the document view, typically a data-entry form, so the next record can be typed. Almost extinct outside intranet applications, and like 204 it carries no body. |
| 206 | Partial Contentcacheable | RFC 9110 §15.3.7 | The body is the byte range the client asked for. | The status behind resumable downloads and video seeking, sent in reply to a Range header and required to carry Content-Range. Answering a Range request with a plain 200 is legal but forces the client to re-download from byte zero. |
| 207 | Multi-Status | RFC 4918 §11.1 | One response body holding a separate status per sub-resource. | WebDAV's answer to bulk operations: the outer status is 207 and the XML body reports 200, 403 or 424 per member. Useful as a design pattern for any batch endpoint, though outside WebDAV you are inventing the body format yourself. |
| 208 | Already Reported | RFC 5842 §7.1 | This member was already listed earlier in the same 207 body. | Exists purely to stop a WebDAV binding loop from repeating an entire subtree in one Multi-Status response. You will never send it by hand. |
| 226 | IM Useddormant | RFC 3229 §10.4.1 | The body is a delta against a copy the client already holds. | Delta encoding for HTTP, proposed in 2002 and effectively never deployed. The idea returned as HTTP compression dictionaries rather than as this status. |
3xx redirection
Further action is needed to complete the request. Four of these nine codes are redirects a browser follows, and the split between them is about method rewriting and cache lifetime.
| Code | Name | Defined in | What it means | When to send it |
|---|---|---|---|---|
| 300 | Multiple Choicescacheable | RFC 9110 §15.4.1 | Several representations exist; the client picks one. | Agent-driven content negotiation, which browsers never implemented a UI for. The practical use is a machine-readable list of alternatives at a versioned API root. |
| 301 | Moved Permanentlycacheable | RFC 9110 §15.4.2 | The resource has a new URL and always will. | Use for domain moves, http→https and trailing-slash canonicalisation, where search engines should transfer ranking signals to the target. Two consequences bite: browsers cache it aggressively and by default indefinitely, and clients are permitted to rewrite POST to GET — send 308 when the method must survive. |
| 302 | Found | RFC 9110 §15.4.3 | The resource is temporarily somewhere else. | The default in most frameworks and rarely the code you actually mean. Every browser rewrites POST to GET on a 302 even though the spec forbids it, so the standard split it into 303 (rewrite the method, deliberately) and 307 (never rewrite it). |
| 303 | See Other | RFC 9110 §15.4.4 | Go and GET this other URL to see the result. | The correct end of a POST-Redirect-GET cycle: the form submission succeeded, and the client must fetch the confirmation page with GET so a browser refresh cannot resubmit the order. Method rewriting is the point of this code, not an accident of it. |
| 304 | Not Modifiedno body | RFC 9110 §15.4.5 | Your cached copy is still current; reuse it. | Answers a conditional GET carrying If-None-Match or If-Modified-Since. It must have no body and must repeat the validators, and it is the reason a well-tuned site can serve a 200 KB page in a 150-byte response. |
| 305 | Use Proxydormant | RFC 9110 §15.4.6 | Repeat the request through the proxy in Location. | Deprecated for being a security hazard — it let any server redirect a client through a machine of the server's choosing. Browsers ignore it. |
| 306 | (Unused)dormant | RFC 9110 §15.4.7 | Reserved; was 'Switch Proxy' in a 1998 draft. | Reserved by the standard and permanently unassigned. Nothing may use it, which makes it a small reminder that gaps in the numbering are deliberate. |
| 307 | Temporary Redirect | RFC 9110 §15.4.8 | Temporarily elsewhere, and keep the method and body. | 302 with the ambiguity removed: a POST redirected with 307 arrives at the target as a POST with the same body. Use it for maintenance windows, regional failover and A/B routing where the client must not silently turn a write into a read. |
| 308 | Permanent Redirectcacheable | RFC 9110 §15.4.9 | Permanently elsewhere, and keep the method and body. | The one to use for a permanent API move, because 301 lets a client turn the POST into a GET and lose the body. It is also what the HSTS preload list effectively assumes; support landed in every browser by 2016. |
4xx client error
The request is at fault and repeating it unchanged will fail again. Picking precisely here is what lets a client library retry, re-authenticate or give up without guessing.
| Code | Name | Defined in | What it means | When to send it |
|---|---|---|---|---|
| 400 | Bad Request | RFC 9110 §15.5.1 | The request itself is malformed and cannot be parsed. | Reserve it for syntax: truncated JSON, a header the parser choked on, a query string that is not valid percent-encoding. A body that parses cleanly but fails your business rules is 422, and using 400 for both denies the client any way to tell a typo from a rejected value. |
| 401 | Unauthorized | RFC 9110 §15.5.2 | No valid credentials were supplied — authenticate and retry. | Misnamed: it means unauthenticated. It is the only 4xx that is required to carry a WWW-Authenticate header naming the scheme, and omitting that header is the most common way a 401 confuses a client library into not retrying with a refreshed token. |
| 402 | Payment Required | RFC 9110 §15.5.3 | Reserved for future use. | Never standardised, but adopted in practice by Stripe, GitHub and several API gateways for 'your plan does not cover this'. Nothing in the ecosystem agrees on the body format, so document yours. |
| 403 | Forbidden | RFC 9110 §15.5.4 | The server understood who you are and refuses anyway. | Send it when authentication succeeded but authorisation failed, and repeating the request with better credentials of the same identity will not help. Where even acknowledging the resource exists leaks information, 404 is the accepted substitute. |
| 404 | Not Foundcacheable | RFC 9110 §15.5.5 | No representation exists for this target, and no reason is given. | Deliberately non-committal: the resource may never have existed, may be hidden, may be back tomorrow. Heuristically cacheable, which surprises people whose freshly deployed route stays missing for a minute behind a CDN. |
| 405 | Method Not Allowedcacheable | RFC 9110 §15.5.6 | The URL exists but not for this method. | Must include an Allow header listing the methods that do work — a 405 without it is a spec violation and a dead end for the caller. A method you have never heard of is 501, not 405. |
| 406 | Not Acceptable | RFC 9110 §15.5.7 | Nothing you can produce matches the Accept header. | The spec explicitly allows ignoring Accept and sending your default representation instead, which is what most servers sensibly do. Sending 406 because a client asked for text/csv and you only have JSON is correct but rarely helpful. |
| 407 | Proxy Authentication Required | RFC 9110 §15.5.8 | The proxy in front of the server wants credentials. | 401's counterpart one hop earlier, carrying Proxy-Authenticate rather than WWW-Authenticate. If your CI jobs fail with this, the fix is in the runner's proxy configuration, not in the application. |
| 408 | Request Timeout | RFC 9110 §15.5.9 | The client took too long to finish sending the request. | Sent when an idle connection is reaped mid-request, which makes it one of the few 4xx codes worth retrying automatically. Do not use it for a request that arrived fine and then took too long to process — that is 504 or a 503 with Retry-After. |
| 409 | Conflict | RFC 9110 §15.5.10 | The request collides with the current state of the resource. | The right code for a duplicate signup, a stale optimistic-lock version or a branch that cannot fast-forward. The body should say what conflicts, because 409 promises the client could resolve it and resubmit. |
| 410 | Gonecacheable | RFC 9110 §15.5.11 | It existed, it was removed on purpose, and it is not coming back. | Stronger and more useful than 404 when you control the history: Google drops a 410 URL from the index faster than a 404, and crawlers stop retrying sooner. Only send it when the removal is deliberate and permanent. |
| 411 | Length Required | RFC 9110 §15.5.12 | Refusing a request body that arrived without Content-Length. | Sent by servers that will not accept chunked transfer encoding on uploads, usually for quota enforcement. In HTTP/2 and HTTP/3 there is no chunked encoding at all, so this code is largely an HTTP/1.1 artefact. |
| 412 | Precondition Failed | RFC 9110 §15.5.13 | An If-Match or If-Unmodified-Since condition was false. | The engine behind safe concurrent writes: the client sends If-Match with the ETag it read, and 412 means someone else changed the resource first. Answer with the current ETag so the client can re-read, merge and retry. |
| 413 | Content Too Large | RFC 9110 §15.5.14 | The request body exceeds what the server will accept. | Renamed from 'Payload Too Large' in RFC 9110. Usually thrown by the reverse proxy rather than your application — nginx client_max_body_size defaults to 1 MB — so check the proxy before debugging the handler. |
| 414 | URI Too Longcacheable | RFC 9110 §15.5.15 | The request target exceeds what the server will parse. | Nearly always a GET that should have been a POST, or a redirect loop appending a parameter each time round. Common ceilings: nginx 8 KB per request line, Apache 8,190 bytes, IIS 16 KB. |
| 415 | Unsupported Media Type | RFC 9110 §15.5.16 | The body's Content-Type is one this endpoint cannot read. | About the request, where 406 is about the response. The classic cause is a curl command with -d and no header, because curl then labels the body application/x-www-form-urlencoded and a JSON API rejects it. |
| 416 | Range Not Satisfiable | RFC 9110 §15.5.17 | The requested byte range lies outside the resource. | Send Content-Range: bytes */<length> with it so the client learns the real size and can ask again. A media player that stalls at the end of a file is often looking at one of these. |
| 417 | Expectation Failed | RFC 9110 §15.5.18 | The Expect header asked for something unsupported. | In practice always about Expect: 100-continue. Some clients retry without the header on receiving it, which is why the failure often looks like a one-off latency spike rather than an error. |
| 418 | I'm a Teapotdormant | RFC 2324 §2.3.2 | The entity is a teapot and cannot brew coffee. | From the Hyper Text Coffee Pot Control Protocol, an April Fools' RFC from 1998 that RFC 7168 extended to teapots. RFC 9110 lists 418 as reserved and unusable precisely because the joke made it too well known to reassign, and Node, Go and Rust all ship the constant. |
| 421 | Misdirected Request | RFC 9110 §15.5.20 | This server cannot produce a response for that authority. | An HTTP/2 phenomenon: browsers coalesce connections when one certificate covers several hostnames, so a request for a.example.com can arrive on a connection opened to b.example.com. Returning 421 tells the client to open a fresh connection and retry. |
| 422 | Unprocessable Content | RFC 9110 §15.5.21 | The syntax is fine but the content is semantically wrong. | The validation-failure code: well-formed JSON whose email field is not an email, or whose end date precedes its start date. Promoted from WebDAV into core HTTP by RFC 9110, so it no longer needs an apology in an API design review. |
| 423 | Locked | RFC 4918 §11.3 | The resource is locked by a WebDAV LOCK. | WebDAV-specific, but reused by document editors and CMSes for 'someone else has this open'. Say who holds the lock and until when, or the client can only poll. |
| 424 | Failed Dependency | RFC 4918 §11.4 | This step failed because an earlier one in the same request did. | Appears inside 207 bodies for batch operations, and has been borrowed by transactional APIs to mark the steps that were never attempted. It says nothing was wrong with this step itself. |
| 425 | Too Early | RFC 8470 §5.2 | Refusing a request replayed from TLS 1.3 early data. | 0-RTT data can be captured and replayed by an attacker, so a server rejects non-idempotent requests that arrive in it. The client retries once the handshake completes, which costs one round trip and removes the replay window. |
| 426 | Upgrade Required | RFC 9110 §15.5.22 | Use a different protocol version to talk to this endpoint. | Must carry an Upgrade header naming what to switch to. Its live use is forcing TLS or a newer HTTP version on a plaintext client, not versioning your own API. |
| 428 | Precondition Required | RFC 6585 §3 | The request must be conditional; send If-Match. | Closes the lost-update hole by refusing unconditional writes outright, rather than waiting for two clients to overwrite each other. Pair it with 412: 428 demands the precondition, 412 reports that it failed. |
| 429 | Too Many Requests | RFC 6585 §4 | The client exceeded a rate limit. | Send Retry-After — as seconds or an HTTP date — or every client will invent its own backoff, and the aggressive ones will win. RFC 9110 does not define RateLimit headers; the draft-standard RateLimit-Limit and RateLimit-Remaining are still an Internet-Draft. |
| 431 | Request Header Fields Too Large | RFC 6585 §5 | The headers, or one of them, are too big to process. | In the wild this is nearly always a cookie jar that grew past the server's 8 KB header budget. The fix that actually works is clearing cookies for the domain; raising the limit only postpones it. |
| 451 | Unavailable For Legal Reasons | RFC 7725 §3 | Access is denied because of a legal demand. | Numbered after Fahrenheit 451 and intended to be visible to researchers measuring censorship. The response should identify the blocking authority in a Link header with rel="blocked-by". |
5xx server error
The server failed to fulfil an apparently valid request. Only these codes and 408, 425 and 429 are safe to retry automatically, and only for idempotent methods.
| Code | Name | Defined in | What it means | When to send it |
|---|---|---|---|---|
| 500 | Internal Server Error | RFC 9110 §15.6.1 | An unhandled failure — the server has no better description. | The catch-all, and a sign of a missing branch whenever it appears in a log with a known cause. Never leak a stack trace in the body; put a correlation id there instead so support can find the trace you kept server-side. |
| 501 | Not Implementedcacheable | RFC 9110 §15.6.2 | The server does not support the method at all, for any resource. | Server-wide, unlike 405 which is per-resource: a server that has never heard of PROPFIND answers 501, while one that knows PATCH but not on this URL answers 405. It is heuristically cacheable, which is why a proxy can keep answering it after you deploy the feature. |
| 502 | Bad Gateway | RFC 9110 §15.6.3 | An upstream server returned something invalid. | Read it as 'the proxy reached your application and disliked the answer' — the process crashed mid-response, closed the socket, or spoke something that was not HTTP. Look at the origin's logs, not the proxy's. |
| 503 | Service Unavailable | RFC 9110 §15.6.4 | Temporarily unable to handle the request, by design. | The only correct status for planned maintenance and for shedding load, and the only 5xx that search engines treat as temporary rather than a defect. Always attach Retry-After; without it, crawlers and clients guess. |
| 504 | Gateway Timeout | RFC 9110 §15.6.5 | An upstream server did not answer in time. | The distinction from 502 is worth keeping: 504 means the origin was still thinking when the proxy gave up, so the work may well have completed afterwards. That makes blind retries of non-idempotent requests dangerous here. |
| 505 | HTTP Version Not Supported | RFC 9110 §15.6.6 | The major HTTP version in the request line is unsupported. | Rare enough that seeing one usually means a protocol-confusion attempt or a client speaking plaintext HTTP/2 to an HTTP/1.1-only port. |
| 506 | Variant Also Negotiatesdormant | RFC 2295 §8.1 | Content negotiation is misconfigured into a loop. | From the experimental transparent content negotiation spec: a chosen variant is itself a negotiation resource, so the server would negotiate forever. Effectively an Apache mod_negotiation configuration error. |
| 507 | Insufficient Storage | RFC 4918 §11.5 | The server cannot store the representation needed to finish. | Literally out of disk or over a quota, for WebDAV and for storage APIs that borrowed it. It says the failure is about capacity, not about your request, so a retry after cleanup is reasonable. |
| 508 | Loop Detected | RFC 5842 §7.2 | An infinite loop was found while processing the request. | The WebDAV binding extension's guard against a directory tree that contains itself. Terminates a request that would otherwise run until the process died. |
| 510 | Not Extendeddormant | RFC 2774 §7 | The request needs an extension the server requires but did not get. | From the experimental HTTP Extension Framework, which never shipped; IANA has since marked it obsolete. Nothing should return it today. |
| 511 | Network Authentication Required | RFC 6585 §6 | A captive portal is intercepting the connection. | Intended for hotel and airport Wi-Fi so that a client can tell a portal from the real origin, instead of caching the login page under your URL. It is defined for the intercepting proxy to send — an origin server must never return it. |
Which one do I send?
Thirteen decisions that account for most of the status codes changed in code review. The right-hand column is the consequence of getting it backwards, which is usually invisible in development and expensive in production.
| Situation | Send | Often sent | What goes wrong |
|---|---|---|---|
| A page moved to a new URL for good | 301 | 302 | 302 tells crawlers to keep the old URL indexed, so ranking signals never transfer to the new one. |
| A POST endpoint moved to a new URL for good | 308 | 301 | A client is allowed to turn a 301-redirected POST into a GET, which silently drops the request body. |
| Traffic is being diverted during a deploy | 307 | 302 | 307 guarantees the method and body survive; every browser rewrites a redirected POST to GET on 302. |
| A form was submitted and the result should be a page | 303 | 200 | Answering the POST directly leaves the browser able to resubmit the order on refresh; 303 sends it to a GET. |
| The caller sent no token, or an expired one | 401 | 403 | 401 plus WWW-Authenticate is the signal that makes a client library refresh the token and retry. |
| The caller is authenticated but lacks the role | 403 | 401 | 401 invites the client to try new credentials, which sends it into a refresh loop it can never win. |
| JSON that parses but fails validation | 422 | 400 | 400 also covers unparseable bytes, so the caller cannot tell a broken payload from a rejected value. |
| The body is truncated or not valid JSON at all | 400 | 422 | 422 says the syntax was fine, which sends the caller looking for a field error that does not exist. |
| The resource was deliberately deleted | 410 | 404 | 410 gets the URL dropped from a search index faster and stops well-behaved crawlers from retrying. |
| The URL exists but not for DELETE | 405 | 404 | 405 with an Allow header tells the caller which methods do work; 404 implies the path itself is wrong. |
| Planned maintenance, back in ten minutes | 503 | 500 | 503 with Retry-After is the only 5xx that search engines and clients treat as temporary by design. |
| The upstream never answered in time | 504 | 502 | 502 means the answer was invalid; 504 means it never arrived, so the work may still be running. |
| The client hit its rate limit | 429 | 503 | 429 is about this caller and carries a per-caller Retry-After; 503 says the whole service is down. |
Codes in your logs that are in no standard
Servers and proxies mint their own numbers, and IANA has never seen any of these 16. Two are not even sent to a client: nginx 444 closes the connection instead of responding, and 499 is written to the access log after the client has already gone.
| Code | Name | Emitted by | What it tells you |
|---|---|---|---|
| 218 | This Is Fine | Apache Web Server | Used by mod_proxy when an error is swallowed and the response is sent anyway. Named after the cartoon. |
| 419 | Page Expired | Laravel | The CSRF token in the form no longer matches the session. Reloading the form fixes it; the code is not registered with IANA. |
| 440 | Login Time-out | Microsoft IIS | The session expired and the client must authenticate again. Outlook Web Access is where most people meet it. |
| 444 | No Response | nginx | An internal instruction, never sent on the wire: nginx closes the connection with no response at all, which is how requests are dropped silently. |
| 460 | Client Closed Connection | AWS Elastic Load Balancing | The client disconnected before the load balancer could respond. Logged by the ALB, not returned to anyone. |
| 463 | Too Many IPs in X-Forwarded-For | AWS Elastic Load Balancing | More than 30 addresses in the X-Forwarded-For header, which usually means a proxy loop. |
| 494 | Request Header Too Large | nginx | nginx's internal code before it converts the failure into a 400 for the client. Raise large_client_header_buffers if it is legitimate. |
| 495 | SSL Certificate Error | nginx | The client certificate presented could not be verified, in mutual-TLS setups. |
| 497 | HTTP Request Sent to HTTPS Port | nginx | A plaintext request arrived on a TLS listener — usually a hard-coded http:// URL against a port that only speaks https. |
| 499 | Client Closed Request | nginx | The single most misread line in an nginx access log: the client gave up first, so a spike of 499s usually means your origin got slow, not that clients broke. |
| 520 | Web Server Returned an Unknown Error | Cloudflare | The origin answered with something Cloudflare could not parse — an empty reply, oversized headers, or a connection reset mid-response. |
| 521 | Web Server Is Down | Cloudflare | The origin refused the TCP connection. Nine times out of ten the origin firewall is blocking Cloudflare's IP ranges. |
| 522 | Connection Timed Out | Cloudflare | The TCP handshake with the origin never completed within 15 seconds, which points at packet loss or a saturated origin rather than at application code. |
| 524 | A Timeout Occurred | Cloudflare | The connection succeeded but the origin did not finish responding within 100 seconds on the free and pro plans. Move long work behind a job queue and answer with 202. |
| 525 | SSL Handshake Failed | Cloudflare | The TLS handshake between Cloudflare and the origin failed — no shared cipher suite, or an origin certificate the chosen SSL mode rejects. |
| 530 | Origin DNS Error | Cloudflare | Always accompanied by a 1xxx error code in the body, and the 1xxx number is the one that tells you what actually broke. |
How to use this status code reference
One number in, one definition out — or read the chart, which is all in the HTML.
Type the number you are looking at
Put the code into the lookup box — 418, or HTTP 429, or status: 502 pasted straight out of a log line, since any three digits in the input are treated as the code. The card gives the name, the RFC section that defines it today, what the code asserts and which neighbouring code you probably meant instead.
Or search the chart by words
Words go to the filter under the chart: type redirect, retry-after, webdav or 9110 and the rows that do not mention it disappear. The class buttons narrow to one hundred-range, and the cacheable tick-box reduces the chart to the 12 codes a shared cache may store with no freshness header at all.
Check the code against its neighbour before you ship it
Scroll to the thirteen comparisons under the chart, which are ordered by how often each mistake reaches code review: 301 against 308, 401 against 403, 400 against 422, 502 against 504. Each row names the situation, the code to send, the code people send instead, and the concrete consequence of getting it the wrong way round.
Technical specifications
| Codes in the chart | 63 registered codes from 100 to 511, plus 16 vendor codes from nginx, Apache, IIS, AWS load balancers, Laravel and Cloudflare |
|---|---|
| Authority cited per row | The document in force today, not the one that introduced the code — RFC 9110 (June 2022) absorbed RFC 7231 and RFC 7538, so 308 is cited as 9110 §15.4.9 |
| Heuristically cacheable | 12 codes: 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 501 — storable by a shared cache with no Cache-Control header present (RFC 9110 §15.1) |
| Must carry no body | 204, 205, 304 and every 1xx; a byte of payload after those headers is a framing error, not a cosmetic one |
| Codes with a mandatory companion header | 401 needs WWW-Authenticate, 405 needs Allow, 407 needs Proxy-Authenticate, 416 needs Content-Range, 426 needs Upgrade, 201 should carry Location and 429 and 503 should carry Retry-After |
| Reserved and unusable | 306, which was Switch Proxy in a 1998 draft, and 418, which RFC 9110 §15.5.19 pins down so the teapot joke can never be reassigned |
| Rendering | Every row is server-rendered HTML — the chart is complete with JavaScript disabled, and the search box, class buttons and lookup card are enhancements on top of it |
| Processing location | Your browser — the lookup runs against a table already in the page, so nothing you type is transmitted anywhere |
Frequently asked questions
When should I use 307 instead of 302?
Use 307 whenever the request might not be a GET. A 307 obliges the client to repeat the original method and body at the new location, while 302 explicitly permits rewriting POST to GET — and every browser does, because that behaviour was so widespread by 1999 that RFC 2616 documented it as an erratum rather than fight it. So a POST to /checkout redirected with 302 arrives as a GET with no body, and the order vanishes. Pick 303 when you want the rewrite on purpose, 307 when the method must survive, and treat 302 as the code you inherited rather than the code you chose.
What is the difference between 401 and 403?
401 means the server does not know who you are; 403 means it knows and still says no. The name is the problem — 401 Unauthorized actually means unauthenticated, which is why it is the only 4xx status required to carry a WWW-Authenticate header naming the scheme the client should use. Retrying a 401 with a refreshed token is reasonable and a well-written client will do it automatically; retrying a 403 with the same identity is pointless, because the answer will not change until someone edits a permission. Where admitting the resource exists would itself leak something, returning 404 in place of 403 is a recognised and accepted practice.
Should a validation error be 400 or 422?
400 for bytes the parser rejected, 422 for a document that parsed and then failed your rules. Truncated JSON, a malformed header, a query string that is not valid percent-encoding: those are 400, because the request could not be understood. A well-formed body whose end date precedes its start date is 422 Unprocessable Content, which RFC 9110 promoted out of WebDAV into core HTTP in 2022 precisely so that API designers would stop apologising for it. Collapsing both into 400 costs the caller the one distinction that decides whether to fix the serialiser or fix the value.
Which status codes are cached by default?
Twelve of them: 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414 and 501. RFC 9110 §15.1 calls these heuristically cacheable, meaning a shared cache is allowed to store and reuse the response even when you sent no Cache-Control and no Expires header. The two that catch people out are 404 and 301 — a mistyped route can stay missing behind a CDN long after the deploy that added it, and a 301 issued by accident is close to permanent because browsers cache it on disk. Send Cache-Control: no-store on error responses you expect to fix.
Which status codes are safe to retry automatically?
408, 425, 429, 500, 502, 503 and 504 — and only for idempotent methods. GET, HEAD, PUT and DELETE can be repeated without changing the outcome; POST cannot, unless you sent an idempotency key. 504 deserves particular care: the gateway gave up waiting, but the origin may have finished the work a second later, so a retried payment can charge twice. Honour Retry-After where the response carries one, and back off exponentially with jitter where it does not, because synchronised retries are how a recovering service is knocked over a second time.
What is the difference between 502, 503 and 504?
502 means the upstream answered with something invalid, 503 means the server is deliberately not serving, and 504 means the upstream never answered in time. Reading them as a chain saves debugging effort: 502 sends you to the origin's logs because the process probably crashed mid-response or closed the socket, 503 is usually your own maintenance flag or load-shedding rule and should always carry Retry-After, and 504 points at a slow query or a saturated upstream rather than at broken code. Cloudflare's 520 and 524 are the same two failures under vendor numbers.
Is 418 I'm a Teapot a real status code?
It is real, it is registered, and it is not usable. RFC 2324 defined it in the Hyper Text Coffee Pot Control Protocol as an April Fools' joke in 1998, RFC 7168 extended the protocol to teapots in 2014, and RFC 9110 §15.5.19 now lists 418 as reserved and permanently unassignable — the joke had become too well known for the number to be given to anything serious. Node.js, Go and Rust all ship the constant, so returning it works; it just means nothing to any client that receives it.
About HTTP status codes
The status line is the oldest surviving piece of HTTP’s design, and its cleverest constraint is that a client only has to understand the first digit. A cache written in 1996 that meets a 308 has never heard of it, reads the leading 3, and treats it as a redirect it cannot follow — which is exactly what the specification requires. That rule is why the registry could grow from the sixteen codes of HTTP/1.0 to the 63 listed here without breaking a single deployed client, and why inventing your own 4xx code is less reckless than it sounds, though the registry almost certainly already has the one you want.
The document to cite in 2026 is RFC 9110, published in June 2022, which consolidated RFC 7230 through 7235 and folded in scattered extensions: 308 arrived from RFC 7538, 422 was promoted out of WebDAV into core HTTP, and 413 was renamed from Payload Too Large to Content Too Large. Codes defined elsewhere kept their homes — 429 and 431 are still RFC 6585, 451 is RFC 7725, and the WebDAV block of 207, 423, 424 and 507 remains RFC 4918. Where a code is dormant the chart says so rather than pretending otherwise: 305 was deprecated as a security hazard, 306 is reserved and unassignable, and 510 has been marked obsolete since the HTTP Extension Framework it belonged to failed to ship.
Most status-code bugs are not exotic. They are a 200 wrapping an error object, which makes every dashboard downstream report a healthy service; a 302 where 307 was meant, which turns a POST into a GET and loses the body; or a 500 for a validation failure, which pages an on-call engineer at three in the morning for a caller’s typo. The comparison table above is the short version of that list. If you are checking what a server actually sent rather than what it should have, rebuild the request with the curl converter and watch the headers in your own client, and when a redirect chain is what you are untangling, put each Location value through the URL parser — a redirect loop is usually one query parameter growing by one hop.
What happens to what you type
Nothing leaves the page. The chart is already in the HTML your browser downloaded, and the lookup box searches that copy in memory — there is no query, no autocomplete request and no log of the code you were curious about.