HTTP Status Codes Reference_
Every HTTP status code, searchable by number or phrase, with what it means, what causes it, and what to do about it. All 63 codes in the IANA registry are here, and so are 24 vendor codes you meet in production logs but cannot look up in a specification — Cloudflare's 52x family, nginx's 499, Laravel's 419 — each labelled with who invented it.
Type 404, type redirect, or press a class chip and read the whole family. Every entry has a permanent address: /http-status-codes#404 goes straight to it.
87 of 87 codes · 63 registered, 24 vendor
100Continue1xx
The server has read your request headers and is willing to receive the body. Sent only when the client asked first by including an Expect: 100-continue header.
- A client uploading a large body that checks before sending it
- curl sending an Expect header automatically on bodies above 1 KB
Nothing to fix — it is a green light. If a proxy chokes on it, send Expect: with an empty value to suppress the ask.
101Switching Protocols1xx
The server is switching this connection to a different protocol at your request. In practice this means a WebSocket handshake succeeding.
- A WebSocket upgrade
- An HTTP/2 upgrade over cleartext
102Processing1xx
103Early Hints1xx
200OK2xx
It worked, and the body is the result. The default answer for a successful GET, and for a POST or PUT that returns content.
- Any request the server understood and fulfilled
201Created2xx
It worked and something new exists because of it. The response should carry a Location header pointing at the new resource.
- A POST that created a record
- A PUT to a URL that did not previously exist
If you are the one returning it, include Location — clients look for it and a 201 without one is a half-answer.
202Accepted2xx
The request was accepted for processing, which has not happened yet and might still fail. An honest answer from an asynchronous API, and a promise you must give the client a way to check on.
- A job queued for a worker
- A batch import handed to a pipeline
Return a status URL the client can poll, or the 202 tells them nothing they can act on.
203Non-Authoritative Information2xx
It worked, but a proxy modified the body on the way to you — the payload is not exactly what the origin sent. Almost never seen deliberately.
- A transforming proxy compressing images or stripping content
204No Content2xx
It worked and there is deliberately nothing to send back. The correct answer to a successful DELETE, and to a PUT the client already has the state for.
- A DELETE that succeeded
- A form submission with no result page
Never send a body with a 204 — it has no length and clients are entitled to stop reading.
205Reset Content2xx
It worked, and the client should clear the form or view that sent the request. A UI instruction rather than a data answer.
- A data-entry form designed for repeated submission
206Partial Content2xx
Here is the slice of the resource you asked for, not the whole thing. The basis of resumable downloads and video seeking.
- A Range request
- A download resuming after a break
- A media player seeking
Check Content-Range in the response before assuming which bytes you got.
207Multi-Status2xx
A WebDAV multi-status: the request touched several resources and the XML body reports a separate status for each. The 207 itself tells you nothing about success.
- A WebDAV PROPFIND or a batch operation across a collection
Parse the body. A 207 with three 200s and a 423 inside is a partial failure wearing a success code.
208Already Reported2xx
226IM Used2xx
300Multiple Choices3xx
Several representations exist and the server will not choose for you. Rarely implemented, because a server that cannot pick usually has a design problem instead.
- Content negotiation with no acceptable default
301Moved Permanently3xx
This URL has moved for good; use the new one from now on. Clients and search engines are entitled to remember it, which makes a mistaken 301 expensive to undo.
- A site restructure
- Forcing HTTPS or a canonical hostname
- A slug rename
Be certain before you send one, and set an explicit Cache-Control — browsers cache 301s aggressively and by default indefinitely. Use 308 if the request method must survive.
302Found3xx
Go here instead, but keep asking the original URL next time. The historical catch, still baked into every browser: a 302 on a POST is turned into a GET, which is not what the spec says and not always what you want.
- Post-then-redirect flows
- Temporary maintenance pages
- Login redirects
If the method must be preserved, use 307. If you meant "the result is elsewhere, go GET it", 303 says so explicitly.
303See Other3xx
Your request was handled; the answer is at this other URL, and you should GET it. The method change is intentional here, which is exactly what distinguishes it from 307.
- The redirect after a successful POST, so a refresh does not resubmit
304Not Modified3xx
Your cached copy is still current — no body sent, save the bandwidth. Triggered by a conditional request carrying If-None-Match or If-Modified-Since.
- A browser revalidating with an ETag
- A CDN checking freshness with the origin
If you see 304 when content has genuinely changed, your ETag is not changing with the content — usually a build that reuses a hash or an mtime that does not move.
305Use Proxy3xx
Deprecated and ignored: it told the client to repeat the request through a specific proxy. Browsers refuse it because an attacker who can inject one can route your traffic.
- Essentially nothing — it is dead
306(Unused)3xx
Reserved and unusable. It was defined in an early HTTP/1.1 draft, withdrawn, and the number is kept reserved so nothing else can claim it.
- Nothing. If you are emitting a 306, something is generating status codes by arithmetic
307Temporary Redirect3xx
Go here instead this time, and do it with the same method and body you used originally. The version of 302 that behaves as written — a POST stays a POST.
- A temporary move where the request is not a plain GET
- HSTS-style forced upgrades
308Permanent Redirect3xx
The permanent partner of 307: moved for good, and the method survives. Introduced because 301 in the wild silently rewrites POST to GET and nothing could be done about that without a new number.
- A permanent move of an endpoint that accepts POST or PUT
Prefer 308 over 301 for API endpoints; prefer 301 for pages, where the wider client support matters more.
400Bad Request4xx
The server could not make sense of the request itself — malformed syntax, not a permission or logic problem. The generic 4xx, and often the laziest one.
- Malformed JSON in the body
- An unencoded character in the query string
- A header the parser rejected
- A framework validation failure returned without a more specific code
Read the response body: a well-built API says which field failed. If the query string is the suspect, percent-encode the values properly. Prefer 422 when the syntax was fine but the content was not.
401Unauthorized4xx
You are not authenticated — the server does not know who you are, or the credentials you sent were rejected. The name says "Unauthorized" and it has misled developers for thirty years; it means unauthenticated. It must carry a WWW-Authenticate header saying how to authenticate.
- A missing or malformed Authorization header
- An expired token
- A signature that does not verify
- A session cookie the server no longer recognises
Authenticate and retry. If you are designing the API: 401 says "log in"; 403 says "logging in will not help". Sending 401 for a permission failure sends clients into a retry loop.
402Payment Required4xx
Reserved for payment and never standardised into anything interoperable. Some APIs now use it for "your plan does not cover this" or "the account is in arrears", which is a reasonable reading of a number the spec left open.
- A metered API on an unpaid account
- A quota tied to billing rather than rate limits
403Forbidden4xx
The server knows who you are and you still may not do this. Authenticating again will not change the answer, which is the whole difference from 401.
- An authenticated user without the required role
- File permissions on the server
- A directory with no index and listing disabled
- A WAF or geo-block rejecting the request
Check the permission model, not the credentials. If a 403 is also intended to conceal existence, return 404 instead — a 403 confirms the resource is there.
404Not Found4xx
Nothing is at this URL. It says nothing about whether the resource ever existed or ever will — which is what makes it both the safest error and the least informative.
- A typo or stale link
- A route that never matched
- A deployment that dropped a file
- A deliberate 404 hiding a resource from someone not allowed to know it exists
For content that has genuinely been removed, 410 says so and is honest with crawlers. For a moved page, 301 is the answer — leaving a 404 loses whatever the old URL had earned.
405Method Not Allowed4xx
The URL exists but not with that verb — a POST to a read-only endpoint, a DELETE where only GET is implemented. The response must list the methods that do work in an Allow header.
- A client using the wrong verb
- A route defined for GET only
- A CORS preflight OPTIONS the server does not handle
Read the Allow header. If OPTIONS is the failing method, the server is missing preflight handling rather than the real route.
406Not Acceptable4xx
The server has the resource but cannot produce it in any form your Accept header will take. Genuinely rare, because most servers send their default and let the client cope.
- An Accept header restricted to a type the server does not produce
- Over-strict content negotiation
Loosen Accept, or drop it — an absent Accept means "anything".
407Proxy Authentication Required4xx
Like 401, but the challenge comes from a proxy between you and the server rather than from the server. Carries Proxy-Authenticate instead of WWW-Authenticate.
- A corporate proxy requiring credentials
- An authenticated egress gateway
Supply Proxy-Authorization. If you did not know a proxy was involved, that is the more interesting finding.
408Request Timeout4xx
The client took too long to send the request and the server gave up waiting. Note the direction: this is the request being slow, not the response.
- An idle keep-alive connection the server reclaimed
- A slow or stalled upload
- A client that opened a connection and sent nothing
Usually safe to retry. Persistent 408s on uploads point at a body larger or slower than the server's timeout allows.
409Conflict4xx
The request is valid but conflicts with the current state — an edit against a version that has since moved, a name already taken, two writers racing.
- Optimistic concurrency detecting a stale version
- A duplicate unique key
- A git-style push against a changed head
Re-read the current state, merge, and retry. Blind retries produce the same conflict.
410Gone4xx
It was here and it is deliberately gone, permanently. Stronger and more useful than 404 because it is a statement of intent — crawlers drop a 410 faster than they drop a 404.
- Content withdrawn on purpose
- An expired campaign URL
- A deleted account
Use it when you know the removal is permanent; leave 404 for "no idea".
411Length Required4xx
The server refuses a request whose body has no declared length. Almost always a chunked-encoding mismatch rather than a client forgetting a header.
- A body sent without Content-Length to a server that requires it
- Chunked transfer to an endpoint that will not accept it
Buffer the body and send Content-Length.
412Precondition Failed4xx
A precondition you attached to the request was false, so the server did nothing. The safety net that makes conditional writes safe.
- If-Match against an ETag that has moved on
- If-Unmodified-Since on a resource that changed
Fetch the current representation, reconcile, and retry with the new validator. A 412 means your write was correctly refused, not that it broke.
413Content Too Large4xx
The body is bigger than the server is willing to accept. Frequently the proxy in front of the application, not the application.
- An upload above the configured limit
- nginx client_max_body_size
- A gateway body cap in front of a service that would have accepted it
Check the proxy limit before the application's. If the response carries Retry-After, the limit is temporary.
414URI Too Long4xx
The URL itself is too long. There is no limit in the spec, so this is entirely about what your server and the intermediaries were built to buffer.
- A GET carrying data that belonged in a body
- A redirect loop appending parameters each time
- A very long filter or search query string
Move the payload into a POST body.
415Unsupported Media Type4xx
The server understands the request but not the format the body arrived in — usually a Content-Type it does not accept, or one that does not match the actual bytes.
- Posting form-encoded data to a JSON-only endpoint
- A missing Content-Type
- An upload whose declared type does not match its content
Set Content-Type to what the endpoint documents. If you are unsure which type a file should declare, look it up rather than guessing.
416Range Not Satisfiable4xx
The byte range you asked for does not exist in the resource — past the end, or reversed.
- A resumed download whose local file is longer than the remote one
- A hand-written Range header
Ask for the whole resource, read its length, then range within it.
417Expectation Failed4xx
The server will not meet the Expect header you sent. In practice this means it does not implement 100-continue and an intermediary passed the expectation on anyway.
- Expect: 100-continue reaching a server or proxy that refuses it
Send the request without the Expect header.
418(Unused)4xx
A joke from a 1998 April Fools RFC about coffee pots, kept alive by developers and by servers that use it to turn away bots. IANA keeps 418 reserved precisely so nothing serious can claim it, which is why the registry calls it "(Unused)" rather than "I'm a Teapot".
- A deliberate joke endpoint
- Some CDNs and WAFs answering automated traffic
419Page Expired4xxLaravel
Laravel's CSRF token check failed — the token in the form no longer matches the session. Not an HTTP standard, just a framework picking an unused number.
- A form left open until the session expired
- A cached page serving a stale token
- A session driver losing state between requests
Reload the form to get a fresh token. If it recurs for everyone, the session store or the page cache is the suspect.
420Enhance Your Calm4xxTwitter API v1
Twitter's rate-limit code before 429 was standardised, named after a line in Demolition Man. Spring once used the same number for "Method Failure", which is why 420 in a log needs context before it means anything.
- Legacy Twitter API clients exceeding a rate limit
Back off. Modern APIs use 429 for this.
421Misdirected Request4xx
The request reached a server that is not configured to answer for that hostname. An HTTP/2 artefact: connections get reused across hostnames that resolve to the same address, and sometimes the far end disagrees.
- HTTP/2 connection coalescing across certificates
- A misrouted virtual host
Retry on a fresh connection; the spec explicitly permits it.
422Unprocessable Content4xx
The syntax parsed fine and the content is still wrong — a well-formed JSON body with an email that is not an email, or a date in the past where the future was required. The precise code for validation failures, which is why so many APIs reach for 400 instead and lose the distinction.
- Schema validation failing on a syntactically valid body
- Business rules rejecting a plausible request
Return which fields failed and why. A bare 422 is barely better than a 400.
423Locked4xx
424Failed Dependency4xx
425Too Early4xx
The server will not risk processing a request that arrived in TLS 1.3 early data, because early data can be replayed by an attacker.
- A 0-RTT request carrying a non-idempotent method
Retry once the handshake completes. Do not send state-changing requests in early data.
426Upgrade Required4xx
The server refuses this request over the current protocol but would accept it over an upgraded one, named in the Upgrade header.
- A plaintext HTTP/1.1 request to an endpoint requiring TLS or HTTP/2
428Precondition Required4xx
The server requires your write to be conditional and yours was not. It is refusing to let you overwrite blindly, which prevents the lost-update problem.
- A PUT without If-Match to an API that mandates optimistic concurrency
GET the resource, take its ETag, and resend with If-Match.
429Too Many Requests4xx
You have sent too many requests in too short a window. Should carry Retry-After telling you how long to wait.
- A client without backoff
- Shared rate limits across a whole team's API key
- A retry storm amplifying an outage
Honour Retry-After. If it is absent, back off exponentially with jitter — synchronised retries are how a rate limit becomes an outage.
430Request Header Fields Too Large4xxShopify
Shopify's variant of 431, used when too many headers arrive or a security rule rejects the request. Shopify also documents it as a security rejection, so the same number covers two situations.
- Oversized cookies on a Shopify storefront
- A request tripping Shopify's protection rules
Trim cookies and headers; if that does not help, the security path is the one that fired.
431Request Header Fields Too Large4xx
The headers are too large, either one of them or all of them together. Cookies are almost always the culprit.
- Cookie accumulation on a shared domain
- An oversized Authorization or Referer header
- A tracking cookie set on every subdomain
Clear cookies for the domain to confirm, then trim what is being set. The failing header is often not the one your request added.
440Login Time-out4xxMicrosoft IIS
The session expired and IIS wants you to authenticate again. Common in SharePoint and older ASP.NET applications.
- An idle session timing out
- An authentication cookie expiring mid-workflow
Log in again. Persistent 440s point at a session timeout shorter than the work takes.
444No Response4xxnginx
nginx closed the connection without answering at all. It never travels over the wire — it only ever appears in nginx's own logs, as a record of a deliberate hang-up.
- A `return 444;` rule dropping malicious or unwanted traffic
- A default server block silently discarding unmatched hosts
Nothing to fix if it is deliberate. If your own client is getting it, you are matching a block rule.
449Retry With4xxMicrosoft IIS
The server wants the request repeated after the client supplies more information. A Microsoft extension that never spread beyond it.
- A Microsoft service asking for additional parameters
450Blocked by Windows Parental Controls4xxMicrosoft
Windows content filtering blocked the request before it reached the network. A local policy decision reported as an HTTP status.
- A family-safety policy on the machine
Adjust the policy on the device — the server never saw the request.
451Unavailable For Legal Reasons4xx
Access is blocked for legal reasons — a court order, a takedown, a sanctions regime. Numbered after Fahrenheit 451, deliberately.
- Geo-blocking for regulatory compliance
- A DMCA or court-ordered removal
- Sanctions screening
Nothing technical. The response should identify who imposed the restriction via a Link header with rel="blocked-by".
460Client Closed Connection4xxAWS Elastic Load Balancing
The load balancer had a response ready and the client had already gone. AWS's counterpart to nginx's 499.
- A user navigating away from a slow page
- A client timeout shorter than the backend's response time
Look at backend latency: 460s cluster around responses slower than clients are willing to wait for.
463Too Many X-Forwarded-For IPs4xxAWS Elastic Load Balancing
The request arrived carrying more than 30 addresses in X-Forwarded-For, which ALB refuses.
- Chained proxies each appending an address
- A forwarding loop
Find the proxy chain. More than a handful of hops usually means a loop rather than a topology.
494Request Header Too Large4xxnginx
nginx's internal code for oversized headers, logged before the standard 431 is returned.
- Cookies exceeding large_client_header_buffers
- A very long query string in the request line
Raise large_client_header_buffers, or trim what is being sent.
495SSL Certificate Error4xxnginx
A client certificate was presented and failed verification. Only appears where mutual TLS is configured.
- An expired or revoked client certificate
- A certificate signed by a CA the server does not trust
Check the client certificate chain and expiry against the server's trusted CA list.
496SSL Certificate Required4xxnginx
Mutual TLS is required and the client presented no certificate at all.
- A client not configured for mTLS reaching an mTLS endpoint
Configure the client certificate. Note the difference from 495: nothing was presented, rather than something invalid.
497HTTP Request Sent to HTTPS Port4xxnginx
A plaintext request arrived on a port expecting TLS. The server can read enough to say so rather than dropping the connection.
- A client using http:// against an https:// port
- A health check missing the scheme
Use https://, or configure a redirect on the plaintext port.
498Invalid Token4xxEsri ArcGIS
An ArcGIS token is expired or malformed. Esri's own numbering, paired with 499 for a missing token in the same product.
- An expired ArcGIS token
Request a new token.
499Client Closed Request4xxnginx
The client disconnected before nginx could answer. Like 444, it exists only in the log — nobody ever received it, because there was nobody left to receive it.
- A user cancelling or navigating away
- A client timeout shorter than the server's response time
- A load balancer giving up on a slow backend
Treat clustered 499s as a latency signal, not a client bug: they mark the point where your responses became slower than callers will tolerate.
500Internal Server Error5xx
Something broke on the server and it will not say what. The catch-all: an unhandled exception is the usual reality behind it.
- An uncaught exception in application code
- A failed database connection
- A misconfiguration that only shows up at runtime
The client cannot fix a 500. Read the server logs — the response body is deliberately vague and the stack trace is not in it.
501Not Implemented5xx
The server does not implement this functionality at all — the method is unrecognised, not merely disallowed here. Different from 405, which means the method exists but not on this URL.
- An exotic verb against a basic server
- A proxy asked to do something it does not support
502Bad Gateway5xx
A server acting as a gateway got an invalid answer from the server behind it. The failure is upstream of whatever answered you.
- An application process that crashed or is not listening
- A reverse proxy pointed at the wrong port
- An upstream returning a malformed response
Check the upstream service first, not the proxy. A 502 usually means the proxy is working correctly and reporting honestly.
503Service Unavailable5xx
The server is temporarily unable to handle the request — overloaded, or deliberately down. The one 5xx that is often intentional.
- Planned maintenance
- An overload shedding load on purpose
- A health check failing so the load balancer has no backend
Retry after the interval in Retry-After. If you are serving it, always set Retry-After — it is the difference between a well-behaved client and a stampede.
504Gateway Timeout5xx
A gateway waited for an upstream server and gave up. The upstream may still be working on it, which is what makes 504s dangerous for non-idempotent requests.
- A slow query behind a proxy timeout
- An upstream in a retry loop of its own
- A timeout set shorter than the work takes
Do not blindly retry a POST — the first attempt may have succeeded after the gateway stopped listening. Use an idempotency key.
505HTTP Version Not Supported5xx
The server refuses the HTTP version in the request line. Nearly always a client speaking to something that predates it, or a protocol confusion.
- An HTTP/2 preface reaching an HTTP/1.0 server
- A malformed request line parsed as a version
506Variant Also Negotiates5xx
507Insufficient Storage5xx
508Loop Detected5xx
509Bandwidth Limit Exceeded5xxApache (mod_bandwidth) / cPanel
A hosting bandwidth quota has been used up. Not an IANA code, but widespread enough on shared hosting that jshttp's statuses package carries it.
- A monthly transfer allowance exhausted
- A traffic spike against a capped plan
Raise the quota or wait for the billing period to reset. A CDN in front usually prevents a repeat.
510Not Extended (OBSOLETED)5xx
511Network Authentication Required5xx
You need to authenticate to the network, not to the site — a captive portal telling you so in a way a machine can recognise. Sent by the intercepting device, never by the origin.
- Hotel and airport Wi-Fi before you accept the terms
- A corporate network requiring sign-in
Open a browser and complete the portal. An API client seeing 511 is on a network it cannot reach the internet from.
520Web Server Returned an Unknown Error5xxCloudflare
Cloudflare reached your origin and got something it could not interpret — an empty reply, a malformed response, or a connection reset mid-answer. The catch-all of the 52x family, and the least specific.
- An origin process crashing mid-response
- Headers too large for Cloudflare to parse
- An origin returning an empty response
Check origin logs for the same timestamp. A 520 means Cloudflare is reporting honestly that your server confused it.
521Web Server Is Down5xxCloudflare
Cloudflare could not open a connection to your origin at all — refused, not merely slow.
- The origin process is not running
- A firewall blocking Cloudflare's address ranges
- The wrong origin IP in DNS after a migration
Confirm the server is listening, then confirm it accepts Cloudflare's IP ranges — blocking them is the most common cause after a move.
522Connection Timed Out5xxCloudflare
The TCP handshake to your origin never completed. Different from 524: this one failed before any request was sent.
- An overloaded origin not accepting connections
- Packet loss or a routing problem
- A firewall dropping rather than rejecting
A dropped packet and a refused connection look different: 522 usually means something is silently discarding traffic.
523Origin Is Unreachable5xxCloudflare
Cloudflare could not route to your origin at all — the address does not resolve or has no path.
- A DNS record pointing at an address that no longer exists
- An origin taken down without updating DNS
Verify the origin record. This is a routing failure, not a server failure.
524A Timeout Occurred5xxCloudflare
The connection succeeded, the request was sent, and your origin did not finish answering within Cloudflare's window (100 seconds on the default plans).
- A long-running report or export
- A slow database query
- A synchronous job that should be asynchronous
Return 202 and a status URL rather than holding the connection. Raising the timeout treats the symptom.
525SSL Handshake Failed5xxCloudflare
TCP connected but the TLS handshake with your origin failed — no shared cipher, or the origin is not serving TLS on that port.
- Full (Strict) mode against an origin with no valid certificate
- A cipher mismatch after a TLS policy change
- Port 443 not actually serving TLS
Check the origin certificate and the SSL mode together — the pair is what has to agree.
526Invalid SSL Certificate5xxCloudflare
The origin presented a certificate that Cloudflare could not validate, while set to Full (Strict).
- An expired origin certificate
- A self-signed certificate under Full (Strict)
- A hostname mismatch
Install a valid certificate on the origin, or switch off Strict — knowing that turns off origin verification.
530Cloudflare Error (see accompanying 1xxx code)5xxCloudflare
A wrapper: the real diagnosis is the four-digit Cloudflare error number shown alongside it, most often 1016 (origin DNS error). The 530 alone tells you nothing.
- A CNAME pointing at a hostname that does not resolve
- A Worker throwing an unhandled exception
Read the 1xxx number in the response body — that is the actual error.
Five classes, one number
What the leading digit commits the server to
A client must understand the class even when it has never heard of the code — meet an unknown 499 and you treat it as a generic 4xx rather than guess. The table below is the whole contract, but two subtleties hide inside it: 2xx means accepted, and 202 accepts only the request, not the work; 3xx means go elsewhere, and the interesting question is whether your method survives the trip. The retry rule follows from the blame: 5xx faults the server and is worth retrying, 4xx faults the request and will fail again unchanged.
The four redirects people mix up
301 and 302 came first and share a defect: browsers turn a redirected POST into a GET, dropping the body. That contradicted the specification but was too widespread to fix, so two new codes were minted to mean what the originals were supposed to. 307 is the temporary one that preserves the method, 308 the permanent one. Where a redirect only ever sees GET traffic the distinction costs nothing, which is why 301 remains right for pages; for an API endpoint accepting POST, 308 is the one that does not silently mangle the request. 303 is the odd one out: it changes the method on purpose, which is what you want after a form submission so a refresh does not resubmit.
401 is not "unauthorized"
The most expensive naming mistake in the protocol. 401 Unauthorized means unauthenticated: the server does not know who you are. 403 Forbidden means it knows and the answer is still no. The test is whether logging in again could change the outcome — if yes, 401; if no, 403. Sending 401 for a permission failure puts well-behaved clients into a re-authentication loop that can never succeed, and every retry hits your auth server. A third option is worth knowing: when the resource's existence is itself privileged, 404 is right, because a 403 confirms something is there.
Choosing a code for an API you are building
Three rules cover most of it. Return 201 with a Location header when you create something, not a bare 200 — the client should not have to guess the new URL. Use 422 when a well-formed request fails validation and keep 400 for malformed input; collapsing both into 400 discards the one bit of information the client needed. When work is asynchronous, say so with 202 and hand back a URL to poll, rather than holding the connection until a gateway times out at 504 and leaves the caller unsure whether the job started. The status code is an interface, and a vague one costs your callers real debugging time.
How to use
- 01Type a number to jump to it — 4 narrows to the client errors, 40 to the 400s, 404 to one entry. Digits match the code, never the prose.
- 02Or type words: a reason phrase like "not found", an old name like "unprocessable entity", or a symptom like "timeout".
- 03Use the class chips to read a family end to end; untick vendor codes for the registry alone. Chips and search combine.
- 04Open an entry for the causes, the fix and the RFC, then Copy to paste it into a ticket. Link a colleague straight there with /http-status-codes#404.
Four ways this gets used
A failing request at 2am
The log says 502 and you need to know whose problem it is before reading code.
502
the upstream failed, not the proxy — check the app process
Picking a code for your own API
The body parsed but the email is invalid. 400 or 422? The entries state the difference and the cost of getting it wrong.
400 vs 422
422 — syntax was fine, content was not
A code that is in no specification
Cloudflare returned 521 and the RFCs say nothing about it, because it is not theirs.
521
the origin refused the connection — usually a firewall
Explaining a redirect to whoever owns SEO
You need 301, 302 and 308 in a sentence each, with something to link to.
3xx
the family, side by side, each with its own anchor
The five classes at a glance
| Class | Name | What it commits the server to | Retry? |
|---|---|---|---|
1xx | Informational | Interim — the real response is still coming | — |
2xx | Success | Received, understood and accepted | No need |
3xx | Redirection | Look elsewhere — usually the Location header | Follow, do not retry |
4xx | Client error | The request was wrong — the same request fails again | Only after changing it |
5xx | Server error | The request was fine and the server failed to fulfil it | Yes, with backoff |
A client meeting an unfamiliar code must fall back to its class — the rule that lets new codes be added without breaking what is already deployed.
The redirects, side by side
| Code | Permanent? | Method preserved? | Use it when |
|---|---|---|---|
301 | Yes | No — POST becomes GET | A page moved for good; cached hard, so be sure |
302 | No | No — POST becomes GET | A temporary move where everything is a GET anyway |
303 | No | No — deliberately becomes GET | After a POST, so refreshing does not resubmit |
307 | No | Yes | A temporary move that must keep method and body |
308 | Yes | Yes | An API endpoint that moved for good and takes POST |
307 and 308 exist only because 301 and 302 were implemented the wrong way round and could not be corrected in place.
Tips & best practices
- Search by symptom, not only by number: "timeout", "certificate" and "cloudflare" land on the family you are chasing.
- Untick vendor codes when writing a spec or a client library — mixing invented codes into a conformance list is how they end up in someone's switch statement.
- Retry 5xx with exponential backoff and jitter; retry 4xx only after changing something. Synchronised retries turn one slow backend into an outage.
- Always send
Retry-Afterwith a 429 or 503. Without it every client invents an interval, and they all pick the same round number. - Reading an API response body? Paste it into the JSON formatter — the useful error detail usually lives there, not in the status line.
- Link to the anchor rather than the page:
/http-status-codes#422lands on the entry, not the top of a long list.
Gotchas — what status codes hide
401 means unauthenticated, whatever the word says
The registered phrase is "Unauthorized" and it means the server does not know who you are. Permission failures are 403. Get it backwards and clients lacking rights re-authenticate, succeed, retry, fail identically and loop — hammering your auth server with requests that were never going to work. The test: could logging in again change the answer?
302 rewrites your POST into a GET
Every browser turns a redirected POST into a GET on a 302, dropping the body. It contradicts the specification and was too entrenched to fix, which is precisely why 307 and 308 were minted. If a redirect might see anything but a GET, use one of those — otherwise the request arrives as something you did not send.
200 with an error inside is the worst answer
An API returning 200 with {"error": "not found"} in the body makes every client parse prose to find out whether the call worked. Monitoring reads it as success, retry logic never fires, and load balancers keep a broken backend in rotation. The status line is the machine-readable part — if the answer is "no", say so in the number.
301 is cached far longer than you expect
Browsers may cache a 301 indefinitely and often do, with no expiry unless you set one. Redirect a URL by mistake and everyone who visited during the mistake keeps being redirected after you fix it — there is no way to reach their cache. Send an explicit Cache-Control with any 301, and use 302 while you are still unsure.
404 and 410 say different things to a crawler
Both mean "not here", but 410 adds "and it is not coming back". Search engines drop a 410 considerably faster, while a 404 is treated as possibly temporary and revisited for a long time. If you removed content deliberately, 410 is honest; if it moved, neither is right — 301 to the new location keeps what the old URL had earned.
The code you are reading may never have reached your server
A CDN, load balancer or WAF can answer on your behalf, and its codes look exactly like yours in a browser. Cloudflare 520-527, nginx 499 and ALB 460 come from the intermediary; your origin logs show nothing at that timestamp, which is the giveaway. Confirm the request arrived before debugging code.
Technical details
- Coverage
- 87 codes — all 63 in the IANA registry plus 24 from Cloudflare, nginx, AWS, IIS, Laravel, Esri, Shopify and Apache
- Source
- The IANA HTTP Status Code Registry via a snapshot captured 2026-07-31, cross-validated at build time against the
statusespackage, failing the build on any disagreement - Explanations
- Written for this page — nothing copied from MDN or Wikipedia, whose paraphrase of the spec is what every other reference already offers
- References
- Registered codes deep-link to their RFC section; vendor codes link that vendor's own documentation
- Search
- Digits match the code by prefix; text matches the phrase, older names and the explanation. Class chips and the vendor toggle compose with it
- Anchors
- Every entry has a permanent id —
#404opens and highlights it - Without JavaScript
- Every code is in the HTML at build time and every entry opens natively; only search and filtering need script
- Network
- None from tool code. A test sweep calls every function this page uses with
fetchandXMLHttpRequestreplaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.
Frequently asked questions
What are the HTTP status codes?
Three-digit numbers a server returns to say what happened to a request. The first digit is the class — 1xx interim, 2xx success, 3xx redirect, 4xx your request was wrong, 5xx the server failed — and a client that does not recognise a code must fall back to its class. 63 are registered with IANA; this page also carries 24 that vendors invented and never registered.
What is the difference between 401 and 403?
401 means the server does not know who you are — authenticate and retry. 403 means it knows and is refusing anyway, so authenticating again will not help. The test is whether logging in could change the outcome. If the resource's existence is itself private, 404 beats both.
When should I use 301 instead of 302?
301 when the move is permanent and you are certain, because browsers cache it hard and may never ask again; 302 while you are still deciding. If the endpoint accepts anything other than GET, use 308 or 307 — 301 and 302 both turn a redirected POST into a GET and drop the body.
What is a 422 and how is it different from a 400?
400 means the server could not parse the request. 422 means it parsed fine and the content is still wrong — valid JSON with an invalid email address. Returning 400 for both discards the distinction the client needed: fix the syntax, or fix the data.
Why am I getting a Cloudflare 521 or 522?
Neither came from your server. 521 means Cloudflare could not open a connection to your origin — usually the process is down or a firewall is blocking Cloudflare's IP ranges. 522 means the attempt timed out, pointing at something dropping packets rather than refusing them. Your origin logs are empty at that timestamp either way.
Which codes should my API return?
201 with a Location header when you create something; 204 for a successful delete; 422 for validation failures and 400 only for malformed input; 202 plus a status URL for async work; 429 with Retry-After when you rate-limit. The rule behind all of them: do not make callers parse prose to learn whether the call worked.