HTTP Status Code Reference
Every registered HTTP status code, plus the unofficial ones you'll meet in nginx, Cloudflare and AWS logs. Search by number, name or problem (redirect, rate limit, timeout, auth), then open a code to see when to use it, what usually causes it and which headers go with it.
503 or a word like timeout.The five classes of HTTP status codes
The first digit of a status code tells you its class, and a client that doesn't recognise a specific code must treat it as the x00 code of that class. An unknown 479 is handled like 400, and an unknown 599 like 500.
- 1xx Informational: interim responses sent before the final one, such as
100 Continueor103 Early Hints. Most HTTP libraries handle them and never show them to you. - 2xx Success: the request was received, understood and accepted.
- 3xx Redirection: the client has to do something else to finish the request, usually follow the
Locationheader or use its cached copy (304). - 4xx Client error: the request was wrong: bad syntax, missing credentials, no permission, a resource that doesn't exist, or too many requests. Sending the same request again unchanged usually fails the same way.
- 5xx Server error: the request may have been fine, but the server or something behind it failed. Retrying later, with backoff, can work.
Choosing status codes for a REST API
Most APIs only need about a dozen codes. Use them consistently and put the details in a machine-readable error body, for example application/problem+json from RFC 9457 with type, title, status and detail fields.
- Reads:
200with the resource,404if it doesn't exist,304when you supportETagand the client already has the current version. - Creates:
201 Createdwith aLocationheader pointing at the new resource. If the work happens later, use202 Acceptedwith a status URL. - Updates and deletes:
200if you return the updated resource,204 No Contentif you don't. Use409for state conflicts and412when anIf-Matchprecondition fails. - Bad input:
400for malformed requests (invalid JSON, wrong types). Some teams use422for well-formed requests that fail validation rules. Either works if you pick one and stick to it. - Auth:
401when credentials are missing or invalid,403when the caller is known but not allowed. To hide that a resource exists at all, return404instead of403. - Limits:
429withRetry-Afterfor rate limiting,413for bodies that are too large. - Failures:
500for unexpected bugs and503withRetry-Afterfor planned downtime or overload. Never return200with an error message in the body, because caches, monitoring, retries and load-balancer health checks all trust the status code.
Common confusions
- 401 vs 403: 401 means "I don't know who you are" (log in and try again) and must include
WWW-Authenticate. 403 means "I know who you are, and the answer is no." - 301 vs 302 vs 307 vs 308: 301 and 308 are permanent, 302 and 307 temporary. With 307 and 308 the client must repeat the same method and body. With 301 and 302, clients may turn a
POSTinto aGET, and browsers do. Use 308/307 for APIs, 301 for permanent page moves, and 303 to send a browser to a result page after a formPOST. - 400 vs 422: 400 is for requests the server can't parse. 422 is for requests it can parse but won't accept, such as an email field that isn't an email.
- 404 vs 410: 410 says the resource is gone for good, and search engines drop it faster. 404 makes no promise either way.
- 502 vs 503 vs 504: all three usually come from a proxy or load balancer, not your code. 502 means the upstream sent back garbage or dropped the connection. 503 means no healthy upstream was available, or the service is shedding load. 504 means the upstream took too long to answer.
- 408 vs 504: 408 means the client was too slow sending its request. 504 means a server behind the proxy was too slow answering.
- 429 vs 503: 429 means "you, specifically, are sending too much". 503 means the service is struggling for everyone.
Status codes on AWS
On AWS, the component returning a code is often not your application. A 502, 503 or 504 from an Application Load Balancer, API Gateway or CloudFront usually points to a timeout, health check or TLS setting rather than a bug. Check the response headers (x-amzn-ErrorType, x-amz-cf-id, Server: awselb/2.0) and the service's access logs to see which layer produced the code. The elb_status_code and target_status_code fields in ALB logs separate the load balancer's code from your target's. S3 is a special case: a missing object returns 403 AccessDenied instead of 404 when the caller lacks s3:ListBucket, so S3 doesn't leak which keys exist.
Which responses are cacheable by default?
RFC 9110 lists the codes that caches may store heuristically, without explicit freshness information: 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414 and 501. Any other code is cached only when the response carries Cache-Control or Expires. This is why a mistaken 301 can stick in browsers long after you fix the server. CDNs add their own rules: CloudFront, for example, caches some 4xx and 5xx errors for 10 seconds by default (the error caching minimum TTL).