Guide

An HTTP exchange is one request and one response. The method says what the client wants. The status code says how it went. The headers carry the rules for caching, retries and identity. Two properties of methods matter most: whether they are safe and whether they are idempotent.

Concept

Before you start: a method is the verb on a request, such as GET or POST. A status code is the three-digit number on a response, such as 200 or 404. A header is a Name: value line that carries metadata.

Walking through it: one exchange

In HTTP/1.1, the messages look like this on the wire:

HTTP
GET /v1/orders/42 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOi...

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 61
Cache-Control: private, max-age=0
ETag: "a1b2c3"

{"id":42,"status":"shipped","total_cents":4599,"currency":"USD"}

The Cache-Control: private header means only the user's browser may store this response.

Safe and idempotent

  • Safe means the request does not change server state. Crawlers and prefetchers assume they can send safe requests freely.
  • Idempotent means sending the same request twice has the same effect as sending it once. Clients and proxies may retry idempotent requests automatically after a network error.
MethodSafeIdempotentTypical use
GETYesYesRead a resource
HEADYesYesHeaders only, such as size or ETag
OPTIONSYesYesCapabilities, CORS preflight
PUTNoYesReplace a resource at a known URL
DELETENoYesRemove a resource; deleting twice leaves it deleted
POSTNoNoCreate, or any non-idempotent action
PATCHNoNot guaranteedPartial update

The classic bug: a client retries a timed-out POST /payments and charges the customer twice. Make it safe to retry with an Idempotency-Key header that the server uses to deduplicate (see idempotency). Never use GET for anything that changes state, because a link prefetcher will eventually trigger it.

Status codes that matter

CodeMeaningWhen to use it
200 OKSuccess with a bodyMost reads and updates
201 CreatedResource createdPOST that creates; include Location
204 No ContentSuccess, empty bodyDELETE, or updates that return nothing
301 Moved PermanentlyPermanent redirect, cacheableOld URL to new URL, forever
302 FoundTemporary redirectLogin redirects. Use 307 or 308 to keep the method
304 Not ModifiedYour cached copy is still validConditional GET with a matching ETag
400 Bad RequestMalformed or invalid inputValidation failures
401 UnauthorizedNot authenticatedMissing or invalid credentials
403 ForbiddenAuthenticated but not allowedPermission denied
404 Not FoundNo such resourceAlso used to hide resources from people who may not see them
409 ConflictState conflictDuplicate create, version mismatch
429 Too Many RequestsRate limitedInclude Retry-After
500 Internal Server ErrorBug on the serverUnhandled exception
502 Bad GatewayProxy got a bad response from upstreamUpstream crashed or closed the connection
503 Service UnavailableTemporarily overloaded or downLoad shedding, maintenance; include Retry-After
504 Gateway TimeoutProxy timed out waiting for upstreamUpstream too slow

The 5xx codes point to different causes. A spike in 502s usually means app instances are crashing or closing keep-alive connections the load balancer still considers open. 504s mean the upstream is too slow. 503s mean something is shedding load on purpose.

Clients should retry 429 and 503 with backoff. They should retry 502 and 504 only for idempotent requests.

Common mistakes

  • Returning 200 for errors. Retries, caches and dashboards all read the status code.
  • Using 302 where the method must be kept. Use 307 or 308.
  • Retrying a non-idempotent POST after a timeout. The first request may have succeeded.

Check yourself

  • Why is a retried PUT safe but a retried POST risky? PUT replaces a resource at a known URL, so the second call leaves the same state. POST may create a second resource.
  • A load balancer shows a spike of 502s. What does that usually point to? Upstream instances crashing, or closing connections the load balancer still considers open.

Key takeaways

  • A request has a method, URL and headers. A response has a status code, headers and body.
  • Safe methods change nothing. Idempotent methods give the same result when repeated, which is what makes retries safe.
  • Use status codes honestly, so retries, caches and alerts work.

Further learning

Next, see how HTTP/1.1 and HTTP/2 move these requests over connections, in HTTP/1.1 and HTTP/2 multiplexing.

MDN: HTTP lists every method and status code with examples.