HTTP

HTTP is the request-response protocol carrying almost all traffic in the applications described here: browsers to servers, frontends to APIs, services to each other, and everything through a reverse proxy such as Traefik.

For application work the useful depth is not the specification. It is the handful of behaviours that cause problems when misunderstood.

What is actually worth knowing

Statelessness, and what it costs. Each request is independent and the server retains nothing between them. This is why the protocol scales — any server can handle any request — and why authentication has to travel with every request rather than being established once. JWT exists because of this, and so does the awkwardness of authenticating a websocket, which is the one place the assumption breaks.

Method semantics as promises. GET is safe and cacheable; PUT and DELETE are idempotent; POST is neither. These are contracts that intermediaries act on. A GET with side effects will eventually be called by a prefetcher, a crawler, or a retry, and the resulting bug will look inexplicable. This is the most commonly violated part of the protocol and the source of the strangest failures.

Status codes as a channel. The difference between 401 and 403 is whether the credential was missing or insufficient; between 400 and 422, whether the request was malformed or well-formed but semantically wrong. Clients and proxies act on these. Returning 200 with an error in the body — still common — means every layer above believes the request succeeded.

Caching. Cache-Control, ETag, and conditional requests are the highest-leverage part of HTTP for performance and the most frequently ignored. Most applications either cache nothing or cache by accident.

The parts that bite in practice

Proxy behaviour is where most of my HTTP debugging time has gone. A reverse proxy terminates the connection and opens a new one, so headers may or may not be forwarded, the client IP becomes X-Forwarded-For, and the application’s idea of its own scheme can differ from what the client sees — which breaks redirect URLs in ways that only appear behind the proxy.

Connection upgrade for websockets needs explicit proxy configuration, and timeouts appropriate for a REST request will terminate a long-lived socket.

See also: APIs, REST and GraphQL for what is built on it, Traefik, and JWT authentication.