18HTTP Status Codes — full reference
What you will master here
- Every status class (1xx, 2xx, 3xx, 4xx, 5xx) and the codes that matter
- Methods (GET / POST / PUT / PATCH / DELETE / HEAD / OPTIONS) and idempotency
- The headers SDETs actually use
- Caching: ETag, Cache-Control, If-None-Match, If-Modified-Since
- CORS preflight (OPTIONS) — what triggers it and how to debug
18.1 1xx — Informational
| Code | Meaning | Notes |
|---|---|---|
| 100 Continue | Server received headers, send the body | Used with Expect: 100-continue for large uploads |
| 101 Switching Protocols | Upgrading to WebSocket / HTTP/2 | Returned during WS handshake |
| 103 Early Hints | Preload hints before final response | Modern perf optimization |
18.2 2xx — Success
| Code | Meaning | Common usage |
|---|---|---|
| 200 OK | Generic success | GET response with body, PUT/PATCH success |
| 201 Created | New resource created | POST that creates; often returns Location header |
| 202 Accepted | Request accepted, processing async | Returns job id; client polls for completion |
| 204 No Content | Success, no body | DELETE response, PUT without body |
| 206 Partial Content | Range request fulfilled | Video streaming, resumable downloads |
18.3 3xx — Redirection
| Code | Meaning | SDET notes |
|---|---|---|
| 301 Moved Permanently | URL changed forever | SEO + browser caches the redirect |
| 302 Found | Temporary redirect | Login → dashboard, classic flow |
| 303 See Other | POST-redirect-GET pattern | Tells client to use GET on the new URL |
| 304 Not Modified | Cached version still valid | Returned when If-None-Match matches ETag |
| 307 Temporary Redirect | Like 302 but preserves the method | POST stays POST after redirect |
| 308 Permanent Redirect | Like 301 but preserves method | Newer, replaces 301 in REST APIs |
18.4 4xx — Client error
| Code | Meaning | Test scenarios |
|---|---|---|
| 400 Bad Request | Malformed request | Bad JSON, missing required field |
| 401 Unauthorized | No or invalid credentials | Missing token, expired token |
| 402 Payment Required | Reserved (some APIs use it) | Stripe-style billing errors |
| 403 Forbidden | Authenticated, not allowed | RBAC failure, user hitting /admin |
| 404 Not Found | Resource doesn't exist | Bad id, deleted resource |
| 405 Method Not Allowed | Wrong HTTP verb for this URL | POST to a GET-only endpoint |
| 406 Not Acceptable | Server can't satisfy Accept header | Client asked for XML, server only does JSON |
| 408 Request Timeout | Client didn't finish sending | Slow client or network |
| 409 Conflict | State conflict | Duplicate email signup, version conflict (ETag) |
| 410 Gone | Resource permanently removed | Used for deprecated endpoints |
| 411 Length Required | Missing Content-Length | Rare; some APIs strict on it |
| 413 Payload Too Large | Body bigger than server allows | File upload size limit |
| 414 URI Too Long | URL exceeds server limit | Massive query strings |
| 415 Unsupported Media Type | Wrong Content-Type | Sending XML to a JSON-only endpoint |
| 418 I'm a teapot | April Fools' joke | Real RFC; some APIs use it as easter egg |
| 422 Unprocessable Entity | Validation failed on parsed body | Body parsed but fields invalid (Rails default) |
| 425 Too Early | Server unwilling to risk replay attack | 0-RTT TLS edge case |
| 426 Upgrade Required | Must use a different protocol | HTTP/1.1 →HTTP/2 forced |
| 428 Precondition Required | Server requires conditional headers | Forces If-Match / If-Unmodified-Since |
| 429 Too Many Requests | Rate limited | Returns Retry-After, X-RateLimit-Remaining |
| 431 Request Header Fields Too Large | Headers exceed limit | Bloated cookies / auth headers |
| 451 Unavailable For Legal Reasons | Censorship / DMCA | Geo / content block |
18.5 5xx — Server error
| Code | Meaning | Common cause |
|---|---|---|
| 500 Internal Server Error | Unhandled exception | The bug you file. Server should not show stack to client. |
| 501 Not Implemented | Server doesn't support method | TRACE on locked-down APIs |
| 502 Bad Gateway | Upstream service returned bad response | Microservice chain failure, proxy issue |
| 503 Service Unavailable | Temporarily down / overloaded | Maintenance, autoscaler scaling up |
| 504 Gateway Timeout | Upstream took too long | Slow downstream, network partition |
| 505 HTTP Version Not Supported | Client sent unsupported HTTP version | Very rare |
| 507 Insufficient Storage | Server out of disk | Storage backend full |
| 508 Loop Detected | Infinite redirect / proxy loop | Misconfigured CDN |
| 511 Network Authentication Required | Captive portal | Wi-Fi needs sign-in |
18.6 HTTP Methods + idempotency
| Method | Purpose | Safe | Idempotent | Cacheable |
|---|---|---|---|---|
| GET | Retrieve resource | ✓ | ✓ | ✓ |
| HEAD | Headers only, no body | ✓ | ✓ | ✓ |
| OPTIONS | Discover allowed methods, CORS preflight | ✓ | ✓ | — |
| POST | Create / submit | ✗ | ✗ | only if explicit |
| PUT | Replace | ✗ | ✓ | ✗ |
| PATCH | Partial update | ✗ | not required | ✗ |
| DELETE | Remove | ✗ | ✓ | ✗ |
| TRACE | Echo for diagnostics | ✓ | ✓ | — |
| CONNECT | Set up tunnel (HTTPS via proxy) | — | — | — |
Safe: doesn't modify server state. Idempotent: calling N times has same effect as 1 time. Idempotent methods can be safely retried by clients on network errors — non-idempotent (POST) can't without an idempotency key.
18.7 Headers SDETs actually use
Request headers
| Header | Purpose |
|---|---|
| Accept | What content types the client wants back (application/json) |
| Accept-Language | Preferred languages (en-US,en;q=0.9) |
| Accept-Encoding | Compression accepted (gzip, br) |
| Authorization | Credentials — Bearer, Basic, etc. |
| Content-Type | What format the request body is in |
| Content-Length | Body size in bytes |
| Cookie | Cookies sent to server |
| If-None-Match | ETag — return 304 if unchanged |
| If-Modified-Since | Time-based conditional GET |
| Origin | Origin of cross-origin request (CORS) |
| Referer | The page that initiated the request |
| User-Agent | Browser / client identifier |
| X-Requested-With | Conventional "XMLHttpRequest" marker |
| Idempotency-Key | Client-generated, lets server dedupe retries |
Response headers
| Header | Purpose |
|---|---|
| Content-Type | Format of the response body |
| Content-Length | Body size |
| Cache-Control | Cache rules (no-store, public, max-age=3600) |
| ETag | Opaque version id of the resource |
| Last-Modified | When the resource was last changed |
| Location | URL to redirect to / location of created resource |
| Set-Cookie | Cookies for the browser to store |
| Retry-After | Seconds (or HTTP date) until client should retry |
| X-RateLimit-Limit / Remaining / Reset | Rate limit window state |
| WWW-Authenticate | How to authenticate (returned with 401) |
| Strict-Transport-Security | HSTS — force HTTPS |
| Content-Security-Policy | What resources the page may load |
| X-Frame-Options | Prevent embedding in iframe |
| Access-Control-Allow-Origin | CORS — who can access |
18.8 Caching with ETag
# First request
GET /api/products/42
<-- 200 OK
ETag: "v3"
Cache-Control: max-age=60
{... body ...}
# Second request, within max-age
GET /api/products/42
If-None-Match: "v3"
<-- 304 Not Modified
(no body — client uses cached version)
18.9 CORS preflight (OPTIONS)
Browsers send a preflight OPTIONS request before a "non-simple" cross-origin request (custom headers, non-GET/HEAD/POST, etc.). The server's response decides whether the actual request is allowed.
OPTIONS /api/orders HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type
<-- 204
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: authorization, content-type
Access-Control-Max-Age: 600
Common bugMisconfigured CORS = "fetch failed in browser" but works in Postman. Postman doesn't enforce CORS — only browsers do. Always test from a real origin.
Module 25 — HTTP Q&A bank
200 vs 201 vs 204?
200 — generic success with body (GET, PUT). 201 — new resource created (POST), often returns Location. 204 — success with no body (DELETE, no-body PUT).
301 vs 302 vs 307 vs 308?
301 permanent (cached forever, method may change to GET). 302 temporary (method may change). 307 temporary, preserves method. 308 permanent, preserves method. Use 307/308 for REST APIs to keep POST as POST.
What is 304 Not Modified?
Response to a conditional GET (If-None-Match with ETag, or If-Modified-Since). Server says "your cached copy is still valid, here are no bytes". Saves bandwidth.
401 vs 403?
401 — server doesn't know who you are (no/expired/invalid credentials). 403 — server knows who you are, but you're not allowed (RBAC denied).
400 vs 422?
400 — request couldn't be parsed (malformed JSON, missing required header). 422 — parsed fine but the data is semantically invalid (email field is not an email). Rails/Django often use 422; others may use 400 for both.
What does 429 mean and what headers come with it?
429 Too Many Requests — rate limited. Server typically returns
Retry-After (seconds until you can retry) and X-RateLimit-Limit/Remaining/Reset. Honour them; don't retry tight-loop.502 vs 504?
502 Bad Gateway — upstream service returned an invalid response. 504 Gateway Timeout — upstream didn't respond within the gateway's timeout. Both point to issues behind your direct server.
What is idempotency?
Calling an operation N times has the same effect as calling it once. GET, PUT, DELETE are idempotent; POST is not (each POST may create a new resource). Idempotent calls are safe to retry on network errors.
How do you make POST idempotent?
Idempotency-Key header — client generates a UUID, sends it with each retry of the same logical operation. Server caches the response under that key; retries return the cached response instead of duplicating the side effect.
What is PUT vs PATCH?
PUT replaces the resource entirely with the body (missing fields become null/default). PATCH applies a partial change — only fields in the body are updated.
What's a CORS preflight?
An OPTIONS request the browser sends before a "non-simple" cross-origin request to check if the server allows it. Server returns Access-Control-Allow-Origin / Methods / Headers; if those match the intended request, browser sends the real request.
Why does my fetch fail in the browser but work in Postman?
CORS. Browsers enforce same-origin policy and refuse cross-origin requests unless the server explicitly allows them. Postman is not a browser and doesn't enforce CORS. Configure CORS on the server (Access-Control-Allow-Origin) for the browser origin.
What's HEAD for?
Like GET but server returns only headers, no body. Useful for checking existence, content-length, last-modified, or content-type without downloading.
What's the difference between Cookie and Set-Cookie?
Set-Cookie is a server response header telling the browser to store a cookie. Cookie is a request header where the browser sends previously-stored cookies back. Sessions ride on this pair.
What's Cache-Control: no-store vs no-cache?
no-store: don't cache at all (sensitive data). no-cache: may cache but must revalidate with the server before using. Often confused — no-cache still allows caching, just with a freshness check.
What's an ETag?
Opaque version identifier (string) the server attaches to a resource. Client sends it back in If-None-Match on subsequent requests; server returns 304 if unchanged. Enables efficient cache validation without timestamps.
HTTP/1.1 vs HTTP/2 — key difference for SDETs?
HTTP/2 multiplexes many requests over one TCP connection — no head-of-line blocking, lower latency on busy pages. Most production sites use HTTP/2 today. Tests rarely care, but slow-network simulation should reflect realistic behaviour.
What is HSTS?
HTTP Strict Transport Security — server response header that tells browsers "always use HTTPS for this domain for the next N seconds". Prevents downgrade attacks. Use
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload.