Cheatsheet

HTTP Status Codes

# HTTP Status Codes

Status codes tell the client what happened to its request in one number. This sheet groups them by class and flags the ones that get misused in real APIs — returning 200 on failure is the single biggest offender.

Quick reference

1xx — Informational

Code Name Meaning
100 Continue Client should send the rest of the request body
101 Switching Protocols Server agrees to switch protocol (used for WebSocket upgrade)

2xx — Success

Code Name Meaning
200 OK Standard success
201 Created Resource created (include a `Location` header pointing to it)
202 Accepted Request accepted for async processing, not yet complete
204 No Content Success, no response body (common on `DELETE`)
206 Partial Content Range request fulfilled (video/audio streaming, resumable downloads)

3xx — Redirection

Code Name Meaning
301 Moved Permanently Resource has a new URL permanently; caches/search engines update their link
302 Found Temporary redirect (historically often misused for what should be 303/307)
303 See Other Redirect after a POST — client should GET the new URL
304 Not Modified Cached version is still valid (`If-None-Match`/`If-Modified-Since`)
307 Temporary Redirect Like 302 but guarantees the method/body are preserved
308 Permanent Redirect Like 301 but guarantees the method/body are preserved

4xx — Client error

Code Name Meaning
400 Bad Request Malformed syntax — the server can't even parse the request
401 Unauthorized Not authenticated (despite the name, this is about *auth*, not permission)
403 Forbidden Authenticated, but not allowed to access this resource
404 Not Found Resource doesn't exist (or the server won't say why)
405 Method Not Allowed Right URL, wrong HTTP method — include an `Allow` header
409 Conflict Request conflicts with current state (e.g. version mismatch, duplicate)
410 Gone Resource existed but was intentionally, permanently removed
422 Unprocessable Entity Syntactically valid but semantically wrong (failed validation)
429 Too Many Requests Rate limited — should include a `Retry-After` header

5xx — Server error

Code Name Meaning
500 Internal Server Error Generic catch-all — something broke server-side
502 Bad Gateway Upstream/proxy got an invalid response from the origin server
503 Service Unavailable Server temporarily can't handle the request (overload, maintenance)
504 Gateway Timeout Upstream/proxy didn't get a response from the origin in time

Common patterns

Standard API error envelope

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests, retry after 30s",
    "request_id": "req_8f2a1c"
  }
}

Pair this body with the matching status code — 429 here, not 200 with an "error" field buried in it.

Correct redirect after a form POST

POST /orders          -> 303 See Other
Location: /orders/42

Using 303 (not 302) after a POST guarantees the browser issues a GET for the redirect target instead of potentially re-submitting the POST body.

Conditional GET with caching

GET /report.pdf
If-None-Match: "33a64df5"

304 Not Modified   <- no body sent, client uses its cached copy

Pitfalls

  • Returning 200 OK with an error in the body: this breaks every generic HTTP client, monitoring tool, and cache that inspects the status line — they'll treat a failed request as a success. The status code is the contract; never make callers parse the body to learn if something failed.
  • 401 vs 403 mixed up constantly: 401 Unauthorized means "I don't know who you are" (missing/invalid credentials); 403 Forbidden means "I know who you are, and you're not allowed." Returning 403 for an expired token trains clients to retry uselessly instead of re-authenticating.
  • 404 used for authorization failures: some APIs return 404 instead of 403 to avoid leaking that a resource exists. That's a deliberate security tradeoff, not a mistake — but document it, because it looks like a bug otherwise.
  • 302 changing the method on redirect: older HTTP clients (and some still today) turn a 302-redirected POST into a GET at the new location, silently dropping the request body. Use 307/308 when you need the method and body preserved.
  • No Retry-After on 429/503: without it, well-behaved clients don't know how long to back off and will hammer the endpoint immediately, defeating the point of the rate limit.

Related ZipKit tools

  • JWT Decoder — inspect the claims behind a 401/403 to see exactly why an API rejected a token.
  • CSP Header Builder — build the response headers that usually ship alongside a hardened API's status codes.

Related cheatsheets