Skip to content
ansezz.

▸ Free tool

HTTP Status Codes.

Every status code, what it actually means, and when to reach for it — including the vendor codes that only exist in your logs. Type in the box to filter the whole reference.

▸ Static page

The whole reference is server-rendered HTML. The only script on this page is the filter box, and it never sends anything anywhere.

1xx — informational

A 1xx is a preliminary response. The client keeps waiting and a final status still arrives on the same request, which is why almost nothing in application code emits one — your server, your proxy, or your HTTP library handles all of it.

The one worth knowing about is 103 Early Hints. It lets you ship the browser a list of assets to start fetching while the origin is still assembling the real response, which buys back real latency on any page with a slow first byte and predictable critical assets. Everything else in this class is either automatic or historical.

1xx informational status codes
Code Name What it means
100 Continue

The server has read the request headers and is willing to take the body. A final status still follows on the same request.

Use it when you never write it by hand. It exists so a client that sent Expect: 100-continue can avoid uploading two gigabytes before learning the request would be rejected.

101 Switching Protocols

The connection is leaving HTTP for whatever the Upgrade header named — in practice, WebSocket.

Use it when a protocol handshake, and only from the server side. Over HTTP/2 and HTTP/3 WebSockets use extended CONNECT instead, so you will never see a 101 there.

102 WebDAV Processing

The server is still working on a long WebDAV operation and wants the client to keep waiting.

Use it when essentially never. It only ever applied to WebDAV, and 103 covers the useful half of the idea.

103 Early Hints

A preliminary response carrying Link headers so the browser can start fetching CSS, fonts, and scripts while the origin is still generating the real response.

Use it when your time-to-first-byte is dominated by slow server work and you know the critical assets up front. Cloudflare and Fastly can synthesise it for you; browsers only act on it for navigation requests.

2xx — success

200 is the default, and defaults are where bugs hide. There are four different things "it worked" can mean, and the code you pick is the only cheap way to tell them apart. If a request created something, say 201 and send a Location header pointing at it. If it only queued something, say 202 and send somewhere to poll — a 202 with no follow-up URL is a shrug. If there is genuinely nothing to return, say 204.

204 has two sharp edges worth memorising. A browser navigating to a 204 stays exactly where it is, which is either a neat trick or a baffling bug depending on whether you meant it. And response.json() throws on a 204 because there is no body to parse, so any client that blindly parses every response needs a guard. If your caller needs data back, 200 with a body is the honest answer.

2xx success status codes
Code Name What it means
200 OK

The request succeeded and the body, if there is one, is the result.

Use it when the operation fully succeeded and you are handing back a representation. Not when it half-worked, and never with an error object inside.

201 Created

A new resource now exists. The response should carry a Location header pointing at it, and usually the new representation as the body.

Use it when a POST or PUT created something addressable — POST /orders that returns order 1234. If nothing was created and you only queued work, that is a 202.

202 Accepted

The request was valid and has been queued. Nothing has actually happened yet, and it might still fail.

Use it when async work: you enqueued a job, sent it to a worker, or handed it to a batch pipeline. Always return somewhere to poll — a Location header or a status URL in the body. A bare 202 tells the client nothing it can act on.

203 Rare Non-Authoritative Information

Success, but a transforming proxy in the middle modified the payload, so it is not exactly what the origin sent.

Use it when you are writing a proxy that rewrites responses. Applications do not emit this.

204 No Content

Success, and there is deliberately nothing to send. A 204 must not carry a body at all.

Use it when DELETE succeeded, a PUT saved with nothing worth returning, a preference toggle. Two traps: a browser navigating to a 204 stays on the current page, and fetch's response.json() throws on it. If the caller needs data, return 200 with a body instead.

205 Rare Reset Content

Success, and the client should reset the form or document view that sent the request. Also forbidden from carrying a body.

Use it when almost never outside legacy form workflows. Modern front ends reset their own state.

206 Partial Content

Here is the byte range that was asked for. Requires a Content-Range header describing what was sent.

Use it when range requests — video seeking, resumable downloads, byte-serving PDFs. Your CDN and static file server do this; you rarely hand-write it.

207 WebDAV Multi-Status

One response body carrying a separate status for each of several resources.

Use it when WebDAV. It is also the honest answer for a batch endpoint where individual operations can fail independently — but if you invent your own envelope, do not label it 207.

208 WebDAV Already Reported

Inside a 207 body: this resource was already enumerated earlier in the response, so it is not repeated.

Use it when WebDAV collections only.

226 Rare IM Used

The response is a delta against a version the client already has, per the instance-manipulation extension in RFC 3229.

Use it when practically never — almost nothing implements delta encoding.

▸ Classic mistake #1

200 OK with an error in the body

This is the most expensive habit in API design, and it always starts reasonably: the front end wants a consistent envelope, so every response becomes 200 with a success: false field somewhere inside. What you have actually done is hide the outcome from every piece of infrastructure between you and the caller.

  • Caches store the failure. A 200 is cacheable by default; a 500 is not. Your CDN will happily serve that error to the next thousand users.
  • Retry policies never fire. Every HTTP client, service mesh, and job runner decides whether to retry from the status code. A 200 means "done, move on".
  • Your dashboards lie. Error-rate panels, SLOs, and alerting rules are built on status-code ratios. A 100% success rate during an outage is worse than no monitoring.
  • SDKs report success. Generated clients raise on non-2xx. Yours will hand the caller a malformed object instead.

Keep the envelope if you like it. Put the machine-readable verdict in the status line and the human-readable detail in the body.

3xx — redirection

Every redirect question reduces to two axes: is this permanent, and does the request method survive? 301 and 302 were specified before anyone cared about the second axis, browsers settled on rewriting POST into GET, and the spec eventually gave up and documented the behaviour. 307 and 308 exist purely to give you the same two choices with the method guaranteed intact.

  301 302 307 308
Permanent Yes No No Yes
Method kept No — POST becomes GET No — POST becomes GET Yes, required Yes, required
Cached by default Yes, aggressively No No Yes
Reach for it Page moved, SEO matters Short-lived detour on a GET Temporary move on a write Permanent move on an API

Two more you should know cold. 303 See Other is the only redirect where switching to GET is the deliberate point: it is the post/redirect/get pattern that stops a browser refresh from resubmitting a form, and it is also the right way to hand a client the result URL of a job it POSTed. 304 Not Modified is not really a redirect at all — it is the successful outcome of a conditional request, and it carries validators instead of a body.

One warning about 301: browsers cache it more aggressively than almost anything else, frequently for the life of the profile, and there is no way to reach into a user's browser and undo it. Ship a 302 while you are still unsure, then promote it to 301 once the move is final.

3xx redirection status codes
Code Name What it means
300 Rare Multiple Choices

Several representations exist and the client (or user) should pick one.

Use it when rarely worth the trouble. There is no standard machine-readable format for the choices, so agents cannot act on it.

301 Moved Permanently

This URL is gone for good; use the new one and update your links. Browsers and caches hold on to it hard — often for the life of the profile.

Use it when a permanent move you are certain about. Clients are allowed to rewrite a POST into a GET on a 301, and they do. If the method matters, use 308.

302 Found

A temporary redirect. The original URL still owns the resource and should keep being used.

Use it when a genuinely temporary move. Most frameworks still emit 302 from their redirect() helper — and it inherits the POST-becomes-GET rewrite from 301, so for a form post-back 303 is the code that actually says what you mean.

303 See Other

Go and GET this other URL. Changing the method to GET is mandatory and intentional.

Use it when post/redirect/get — you processed a form and want a refresh not to resubmit it. Also correct for handing a client the result URL of a job it POSTed.

304 Not Modified

The cached copy the client already has is still fresh. No body is sent.

Use it when a conditional request with If-None-Match or If-Modified-Since matched. Send the validators and cache headers, not the payload — that is the whole point.

305 Deprecated Use Proxy

The resource must be fetched through the proxy named in the response.

Use it when never. Deprecated for security reasons and ignored by every current client.

306 Reserved (Unused)

Used in a draft, never standardised, and permanently reserved so nothing else can claim it.

Use it when never.

307 Temporary Redirect

A temporary redirect where the method and the body must be preserved exactly.

Use it when you need to bounce a POST, PUT, or PATCH somewhere else without losing the body. This is the 302 you actually wanted on an API.

308 Permanent Redirect

A permanent redirect where the method and the body must be preserved exactly.

Use it when a permanent move on an API where POST has to stay POST. Also the safer choice for http-to-https canonicalisation on endpoints that accept writes.

▸ Classic mistake #2

Redirect chains

Nobody designs a redirect chain. They accumulate. The TLS rule redirects http://example.com/page to https, the canonical-host rule redirects that to www, the trailing-slash rule redirects that again, and the old-path map adds a fourth hop. Every hop is a full round trip — DNS may already be warm, but the TCP and TLS handshakes are not free on a phone on a train.

  • Collapse to one hop. Compute the final URL in a single rule instead of chaining independent ones. Scheme, host, and path can all be fixed at once.
  • Crawlers give up. Search engines follow a limited number of hops and dilute the signal at each one. Browsers stop at roughly twenty and show an error.
  • Writes get worse. Chain a POST through a 301 or 302 and the body is gone by hop two. If a write can hit the chain, every hop has to be 307 or 308.
  • Test it. curl -sIL prints every hop. If the list is longer than two lines, you have work to do.

4xx — client error

This is where API design actually happens. The useful question is not "what went wrong" but "what should the caller do next?" — and there are only four answers. Retry unchanged and later (429, 425, sometimes 408 and 409). Retry with different credentials (401). Fix the request and retry (400, 405, 413, 415, 422). Or stop entirely, because nothing the client does will change the outcome (403, 404, 410, 451).

Pick the code that answers that question and half of your error handling documentation writes itself. Pick 400 for everything and you have taught every client that the only safe strategy is to give up.

  401 403 404
It says I do not know who you are I know, and no There is nothing here
Required header WWW-Authenticate None None
Retry with new credentials? Yes, that is the point No No
Typical trigger Missing or expired token Role, scope, or WAF rule Bad ID, or hiding a 403

Two more distinctions that cost people days. 404 vs 410: 404 means "nothing here, no comment on history"; 410 means "it was here, I removed it, stop asking". If you have deliberately deleted content and want crawlers to drop it, 410 is the stronger signal — but only use it when the removal is genuinely permanent, because there is no polite way to take it back. 409 vs 412: 409 is a conflict with the current state that the server detected on its own, 412 is a precondition the client attached failing. If your callers send If-Match with an ETag, 412 is the correct answer and 428 is how you force them to send it in the first place.

And on 429: the code is the easy part. What makes a rate limit usable is Retry-After, in seconds or as an HTTP date, on every single 429 you emit. Without it, a well-written client has to guess and a badly-written one retries immediately, which is exactly the traffic you were trying to shed. The RateLimit-* header family is still an IETF draft; ship it if you like, but ship Retry-After first because everything already understands it.

4xx client error status codes
Code Name What it means
400 Bad Request

The server refuses to process the request because the request itself is wrong: unparseable JSON, a malformed query string, a missing required field.

Use it when the payload could not be understood or is structurally invalid. RFC 9110 widened 400 to cover any client error the server cannot classify better, so it is always defensible — but a more specific code is more useful to the caller.

401 Unauthorized

Misnamed: it means unauthenticated. No credentials were supplied, or the ones supplied were not valid.

Use it when the client should authenticate and retry. It must carry a WWW-Authenticate header naming the scheme, and in practice that header is the single most commonly omitted requirement in the whole 4xx block.

402 Rare Payment Required

Reserved since 1997 and quietly real now: Stripe returns it for declined cards, and metered APIs use it for an exhausted plan.

Use it when billing state blocks the call. There is no standard semantics behind it, so document exactly what you mean and what the client should do.

403 Forbidden

The server understood the request and is refusing to authorise it. Re-authenticating is not implied to help.

Use it when an authenticated caller lacks permission, or a WAF, IP allowlist, or policy engine blocked the request. If logging in would fix it, the correct code is 401.

404 Not Found

There is no resource at that URL, and the server is not saying whether there ever was one.

Use it when a route does not exist, an ID does not resolve, or you are deliberately hiding the existence of something the caller has no right to know about. That last case is a legitimate and common substitute for 403.

405 Method Not Allowed

The URL exists but that verb is not supported on it.

Use it when you route by method and this one is not wired up. The response must include an Allow header listing the methods that are — frameworks that skip it turn a five-second fix into a debugging session.

406 Not Acceptable

Nothing the server can produce matches the client's Accept header.

Use it when content negotiation genuinely fails and returning something anyway would be wrong. Most JSON APIs are better off ignoring a nonsense Accept header and serving JSON.

407 Proxy Authentication Required

The 401 of the proxy world. The client must authenticate with the proxy, which sends Proxy-Authenticate.

Use it when you are a proxy. Seeing it as a client usually means a corporate egress proxy is in the path.

408 Request Timeout

The client was too slow sending the request and the server is closing an idle connection.

Use it when you are a server reaping connections. As a client, a 408 is often just keep-alive housekeeping rather than a real error — retry the request once.

409 Conflict

The request clashes with the current state of the resource.

Use it when a duplicate create hits a unique constraint, an edit lands on a stale version, or a delete is blocked by dependents. Say what conflicts in the body — a bare 409 is a support ticket waiting to happen.

410 Gone

It existed, it was deliberately removed, and it is not coming back.

Use it when you intentionally deleted something and want clients and crawlers to drop it. Search engines treat 410 as a slightly stronger removal signal than 404. Only reach for it when the removal really is permanent.

411 Rare Length Required

The request needs a Content-Length header and did not have one.

Use it when you cannot accept a chunked or length-less body on this endpoint.

412 Precondition Failed

A condition the client attached to the request — If-Match, If-Unmodified-Since — did not hold.

Use it when optimistic concurrency: the caller sent an ETag and the resource changed underneath them. This is how you stop lost updates without taking a lock.

413 Content Too Large

The body exceeds what the server is willing to accept. Renamed from Payload Too Large in RFC 9110.

Use it when an upload is over the limit. Check the whole chain before blaming your code — nginx defaults client_max_body_size to 1 MB, and most managed gateways cap request bodies far below what your handler allows.

414 URI Too Long

The request target is longer than the server will parse.

Use it when someone put a filter payload in a query string. It is usually a signal that the operation should have been a POST.

415 Unsupported Media Type

The Content-Type of the body is not one this endpoint accepts.

Use it when form-encoded data arrived at a JSON-only endpoint, or the Content-Type header is missing entirely. Most mysterious empty-body bugs deserve this instead of a vague 400.

416 Range Not Satisfiable

The requested byte range does not exist in the representation.

Use it when you serve range requests and the range is out of bounds. Reply with Content-Range: bytes star-slash-length so the client learns the real size.

417 Rare Expectation Failed

The server will not meet the expectation in the Expect header — in practice, Expect: 100-continue.

Use it when you are a server or proxy that refuses 100-continue.

418 Reserved I'm a Teapot

A 1998 April Fools joke from RFC 2324. IANA keeps it reserved specifically so nothing real can ever claim it.

Use it when never in production. It is funny right up until a CDN, an SDK's error mapper, or an on-call engineer has to deal with it.

421 Misdirected Request

This connection cannot serve requests for that authority — HTTP/2 connection coalescing sent the request to the wrong origin.

Use it when you are terminating HTTP/2 for multiple hostnames on one certificate. The client is expected to retry on a fresh connection.

422 Unprocessable Content

The syntax is fine and the body parsed, but the values are semantically unacceptable.

Use it when validation failed on a well-formed request — an end date before a start date, an email that is not an email. Return field-level errors in the body; RFC 9457 problem details is a ready-made shape for that.

423 WebDAV Locked

The resource is locked by another party.

Use it when WebDAV locking, or a domain model with explicit checkouts.

424 WebDAV Failed Dependency

This request was not attempted because a request it depended on failed.

Use it when WebDAV, and occasionally a batch pipeline where one step gates the next.

425 Too Early

The request arrived in TLS 1.3 early data (0-RTT) and the server will not risk processing a replayable request.

Use it when you terminate TLS with 0-RTT enabled and the request is not idempotent. The client retries once the handshake completes. If you have never enabled 0-RTT, you will never emit this.

426 Upgrade Required

The endpoint requires a different protocol.

Use it when you refuse plain HTTP/1.1 on an endpoint that needs TLS or a newer version. Must include an Upgrade header naming what you want.

428 Precondition Required

The server refuses an unconditional request and wants the client to send If-Match.

Use it when you want to force every writer to prove which version it is editing, so two concurrent updates cannot silently clobber each other. It is the polite half of the 412 story.

429 Too Many Requests

The client has exceeded a rate limit.

Use it when rate limiting or quota enforcement — and always with a Retry-After header, in seconds or as an HTTP date. Without it, well-behaved clients guess and badly-behaved ones hammer you. The RateLimit-* fields are still an IETF draft; Retry-After is the one everything already understands.

431 Request Header Fields Too Large

The headers, individually or in total, exceeded what the server will accept.

Use it when a cookie jar or a bloated JWT blew past the limit. Node defaults to 16 KB of headers; nginx to 8 KB buffers. Fix the token size, do not raise the ceiling.

451 Unavailable For Legal Reasons

Blocked because of a legal demand — a court order, a takedown, a sanctions regime — not a technical failure.

Use it when the law, not your policy, is the reason. Include a Link header with rel=blocked-by naming the entity making the demand. Generic geo-blocking with no legal basis is a 403.

419 Non-standard Page Expired (Laravel)

Not in any RFC. Laravel returns it when the CSRF token is missing, stale, or the session expired.

Use it when you are debugging a Laravel form that suddenly stopped submitting. The fix is session lifetime or a refreshed token, not the status code.

420 Non-standard Enhance Your Calm (legacy Twitter)

Twitter's rate-limit code from before 429 existed. Retired, but still quoted in old client libraries.

Use it when never. Use 429.

430 Non-standard Shopify Security Rejection

Shopify's code for a request its edge security layer rejected before your app ever saw it.

Use it when you are reading Shopify app logs. It is not something you emit.

440 Non-standard Login Time-out (IIS)

Microsoft IIS: the session expired and the client must authenticate again.

Use it when never by hand. Standard equivalent is 401.

444 Non-standard No Response (nginx)

An nginx directive that closes the connection without sending anything at all. It appears in the access log and never on the wire.

Use it when you are dropping abusive traffic at the edge without spending bytes on a response.

449 Non-standard Retry With (IIS)

Microsoft IIS: retry the request after supplying more information.

Use it when never. It has no standard meaning.

460 Non-standard Client Closed Connection (AWS ALB)

The client disconnected before the AWS load balancer could send a response. Log-only, like nginx 499.

Use it when you are reading ALB access logs. It points at client timeouts or slow targets, not at the balancer.

463 Non-standard Too Many X-Forwarded-For IPs (AWS ALB)

The request arrived at an AWS load balancer with more than 30 addresses in X-Forwarded-For.

Use it when you are debugging a proxy chain that keeps appending. Fix the chain.

494 Non-standard Request Header Too Large (nginx)

An nginx-internal code, logged when headers overflow the configured buffers.

Use it when never — nginx converts it to a 400 on the wire. Standard equivalent is 431.

495 Non-standard SSL Certificate Error (nginx)

nginx-internal: the client presented a certificate that failed verification.

Use it when never by hand. It shows up when you enable mutual TLS.

496 Non-standard SSL Certificate Required (nginx)

nginx-internal: mutual TLS is on and the client sent no certificate.

Use it when never by hand. Same family as 495.

497 Non-standard HTTP Request Sent to HTTPS Port (nginx)

nginx-internal: a plaintext request arrived on a TLS listener.

Use it when never by hand. Usually a misconfigured health check or an http:// URL that should be https://.

499 Non-standard Client Closed Request (nginx)

nginx writes this to its access log when the client hung up before a response was ready. It is never sent over the wire — nobody receives a 499.

Use it when you are reading nginx logs. A spike means callers are timing out first, so look at upstream latency and client timeouts, not at nginx. Esri's servers use 499 for a missing token, which is an unrelated collision.

▸ Classic mistake #3

Returning 403 when you meant 401

The names are the problem: 401 is called "Unauthorized" and 403 is called "Forbidden", when the accurate labels would be "Unauthenticated" and "Unauthorized". So middleware that finds no token returns 403, and the client sits there with no idea that refreshing its token would fix everything.

The test is one question: would valid credentials change the answer? If yes, it is 401 — and it must carry a WWW-Authenticate header naming the scheme, which is the requirement almost every API skips. That header is what tells a client library to run its refresh flow instead of surfacing a dead end to the user. If no credentials would ever help, it is 403.

The one legitimate reason to blur this: returning 404 instead of 403 when even confirming that a resource exists leaks something. That is a deliberate trade, and worth a comment in the code so the next person does not "fix" it.

5xx — server error

A 5xx is a promise that the caller did nothing wrong. That is the whole contract, and it is why the 4xx/5xx boundary is the single most load-bearing line in your monitoring: 4xx is traffic, 5xx is an incident. Blur it and your alerting is worthless in both directions.

In production the useful split inside 5xx is who generated it. A 500 came from your application — something threw. A 502 means the proxy in front of it got a response it could not parse, which usually means your process crashed, refused the connection, or died mid-response. A 504 means the proxy waited and gave up, so the process is alive and too slow; the number of seconds it waited is a config value somewhere, not a mystery. And 503 is the only one you should ever emit deliberately: overloaded, draining, or in maintenance, always with Retry-After, because it is the one 5xx that clients and crawlers are meant to treat as temporary.

5xx server error status codes
Code Name What it means
500 Internal Server Error

Something in the server blew up and nothing more specific applies. It is a statement that the caller did nothing wrong.

Use it when an unhandled exception, a null dereference, a broken invariant. Every 500 should map to a log line with a stack trace. Never use it for validation — that is a 4xx, and mixing the two destroys your error budget.

501 Not Implemented

The server does not support the method at all, anywhere — not merely on this URL.

Use it when a gateway meets a verb it has never heard of. If the method exists but not on this route, that is 405.

502 Bad Gateway

A proxy received an invalid, empty, or unparseable response from the server behind it.

Use it when you are the proxy. From outside, a 502 almost always means the app crashed, refused the connection, or wrote garbage — check the application logs, not the proxy's.

503 Service Unavailable

The service is temporarily unable to handle the request: overloaded, draining, starting up, or deliberately in maintenance.

Use it when the condition is temporary and you know roughly for how long — pair it with Retry-After. It is the only 5xx that clients, CDNs, and crawlers are meant to treat as transient, which is why a maintenance window should return 503 and never 500 or 404.

504 Gateway Timeout

A proxy gave up waiting for the server behind it to respond.

Use it when you are the proxy and upstream blew the timeout budget. A 504 landing at exactly 30 or 60 seconds is a configuration value, not a mystery — compare the gateway timeout with the application's own.

505 Rare HTTP Version Not Supported

The major HTTP version in the request is not supported.

Use it when practically never. Almost always a client speaking something that is not HTTP at all.

506 Rare Variant Also Negotiates

A transparent content negotiation misconfiguration on the server side.

Use it when never, unless you are implementing RFC 2295.

507 WebDAV Insufficient Storage

The server cannot store the representation needed to complete the request.

Use it when WebDAV, or an upload service that is genuinely out of disk. Mostly you want 503 with a Retry-After.

508 WebDAV Loop Detected

The server aborted an operation because it found an infinite loop.

Use it when WebDAV bindings. Its useful cousin in application code is a request-depth or recursion guard.

510 Deprecated Not Extended

The request needs further extensions to be processed. The extension framework behind it was made historic.

Use it when never.

511 Network Authentication Required

You are behind a captive portal that wants you to log in to the network.

Use it when you are the intercepting proxy — an origin server must never generate it. The entire point is that a client can tell apart the network hijacking the request and the API saying no.

509 Non-standard Bandwidth Limit Exceeded

An Apache and cPanel extension for an account over its transfer quota.

Use it when never in your own code. Use 429 or 503.

520 Non-standard Unknown Error (Cloudflare)

Cloudflare's catch-all: the origin returned something empty, malformed, or otherwise unintelligible.

Use it when you are debugging a Cloudflare-fronted site. It usually means the origin reset the connection or sent an invalid response — check the origin, not Cloudflare.

521 Non-standard Web Server Is Down (Cloudflare)

The origin actively refused Cloudflare's connection.

Use it when you are reading a Cloudflare error page. The origin process is down or its firewall is dropping Cloudflare's IPs.

522 Non-standard Connection Timed Out (Cloudflare)

The TCP handshake between Cloudflare and the origin never completed.

Use it when network-level diagnosis: routing, security groups, or an overloaded origin accept queue.

523 Non-standard Origin Is Unreachable (Cloudflare)

Cloudflare could not route to the origin at all — usually a bad DNS record or a dead IP.

Use it when check the DNS record behind the proxied hostname.

524 Non-standard A Timeout Occurred (Cloudflare)

The origin accepted the connection but did not finish responding inside Cloudflare's window (100 seconds on the standard plans).

Use it when you have a long-running request behind Cloudflare. Move it to a job and return 202 rather than trying to raise the ceiling.

525 Non-standard SSL Handshake Failed (Cloudflare)

The TLS handshake between Cloudflare and the origin failed.

Use it when you set Full or Strict SSL mode and the origin's TLS config does not agree — ciphers, versions, or SNI.

526 Non-standard Invalid SSL Certificate (Cloudflare)

The origin presented a certificate Cloudflare could not validate in Strict mode.

Use it when expired, self-signed, or wrong-hostname origin certificates. Install a real certificate or an origin certificate from Cloudflare.

527 Non-standard Railgun Error (Cloudflare)

A failure in Cloudflare's retired Railgun transport.

Use it when never — the product is gone. Included because it still turns up in old runbooks.

530 Non-standard Origin DNS Error (Cloudflare)

Always shown alongside a Cloudflare 1xxx error code; the real cause is in that number, most often a Worker exception or an origin DNS failure.

Use it when you see a 530 — go read the 1xxx code on the error page, because the 530 itself tells you nothing.

599 Non-standard Network Connect Timeout Error

A convention some proxies and HTTP clients use for a connection timeout they invented themselves.

Use it when never in your own responses. It exists so client libraries have something to put in a log line.

▸ Classic mistake #4

500 for validation failures

Usually not a decision — a consequence. Validation throws, a global exception handler catches everything it does not recognise, and every bad email address on your signup form becomes a server error. Then the on-call rotation learns to ignore the 500 alert, and the one real outage goes unnoticed for forty minutes.

  • Map your exception types. Validation errors to 422 (or 400), authorisation to 401 or 403, missing records to 404, conflicts to 409. Only the unmapped remainder becomes a 500.
  • Alert on 5xx, never on 4xx. A 4xx rate is a product signal — it tells you a client is broken or your docs are wrong. A 5xx rate is a pager.
  • Every 500 gets a stack trace. If it does not, it was never a 500; it was a 4xx you failed to classify.
  • Do not leak. The response body for a 500 gets a correlation ID and nothing else. The stack trace goes in the log.

Picking a code in thirty seconds

  1. Did the server fail? If your code threw or your dependency is down, it is a 5xx. 500 if it is you, 503 if it is temporary and you can say when, 502 or 504 if you are the proxy reporting on something behind you.
  2. Could the caller fix it? Then it is a 4xx. Missing credentials is 401, insufficient permission is 403, wrong URL is 404, wrong body is 400 or 422, wrong media type is 415, wrong verb is 405, too much of it is 413 or 429.
  3. Did it succeed and create something? 201 with a Location header.
  4. Did it succeed and queue something? 202 with somewhere to poll.
  5. Did it succeed with nothing to say? 204, and no body at all.
  6. Anything else that succeeded? 200 with the representation. And if you catch yourself writing an error field into a 200, go back to step one.

Questions people ask

What is the difference between HTTP 401 and 403?

401 means the server does not know who you are: no credentials, an expired token, a bad signature. It must include a WWW-Authenticate header, and the client is expected to authenticate and retry. 403 means the server knows who you are and is refusing anyway, so retrying with the same credentials will not help. If logging in could fix it, it is a 401.

Should I use a 301 or a 302 redirect?

Use 301 when the URL has moved for good and you want browsers, caches, and search engines to update. Use 302 when the move is temporary and the original URL still owns the content. Both allow clients to rewrite a POST into a GET, so when the method has to survive the redirect use 308 for permanent moves and 307 for temporary ones.

Should validation errors return 400 or 422?

Return 400 when the request could not be parsed or is structurally wrong: broken JSON, a missing required field, an unusable query string. Return 422 when the body parsed cleanly but the values are unacceptable, such as an end date before a start date. Both are defensible under RFC 9110, so what actually matters is picking one rule, applying it on every endpoint, and putting field-level errors in the body.

Is it OK to return HTTP 200 with an error in the body?

No. The status line is the only part of a response that proxies, CDNs, retry policies, SDKs, and dashboards understand without parsing your payload. Returning 200 with an error object means caches store failures, client libraries report success, and your error-rate graph stays flat during an outage. Put the human-readable detail in the body and the machine-readable verdict in the status code.

What does HTTP status 499 mean?

499 is an nginx-only code that never travels over the wire: nginx writes it to its access log when the client closed the connection before a response was ready. It means the caller timed out or navigated away, so investigate upstream latency and client timeouts rather than nginx itself. Esri's servers use 499 for a missing token, which is an unrelated collision.

What status code should a successful POST return?

201 if it created a resource, with a Location header pointing at the new URL. 202 if it only queued work, with somewhere to poll for the result. 200 if it ran a command and has something to return, and 204 if it ran and there is genuinely nothing to send back. A blanket 200 on every POST throws away information the client could have acted on.

Related

Keep reading