HTTP · Engineering Insights
The design decisions, trade-offs, and comparisons that separate knowing HTTP's syntax from understanding why it's built the way it is.
Why HTTP Is Stateless
Statelessness wasn't an oversight — it's the single decision that let the web scale to billions of clients.
If the server had to remember every client between requests, it would need to keep per-client memory alive for the entire session — and any load balancer would need to route every request from a given client back to the exact same server ("sticky sessions"), which destroys the ability to freely scale horizontally or fail over instances.
Because HTTP is stateless, any server behind a load balancer can handle any request. Scaling out is just adding servers; failover is just routing around a dead one. The cost is that applications must externalize state (to a database, Redis, or a signed token) — but that cost makes horizontal scaling and failover far simpler — the remaining scaling limits move to the shared state itself, such as the database.
HTTP/1.1 vs HTTP/2 vs HTTP/3
Each newer version targets a bottleneck in the older one, but real-world gains still depend on latency, packet loss, middleboxes, and implementation quality.
HTTP/1.1
One request in flight per connection. Browsers open ~6 parallel connections per host as a workaround — each with its own TCP + TLS handshake cost. Simple, universally supported, but head-of-line blocked at the application layer.
HTTP/2
Multiplexes many requests over one TCP connection using binary framing. Solves app-level head-of-line blocking and adds header compression (HPACK). Still vulnerable to TCP-level head-of-line blocking — one lost packet stalls every stream on that connection.
HTTP/3
Runs over QUIC (UDP) instead of TCP. Each stream is independent at the transport level, so packet loss on one stream does not stall the others the way a lost TCP packet can stall every HTTP/2 stream sharing that connection. QUIC also combines transport setup with modern TLS, which can reduce connection-establishment latency.
Worked scenario: one lost packet (illustrative)
Assumptions: a page fetches 12 resources at once, RTT is 100 ms, and one packet is lost mid-transfer. HTTP/1.1 spreads the transfers over 6 connections (about 2 resources each); the loss stalls only the connection it hit, so roughly 1/6 (~17%) of in-flight transfers wait for loss recovery. HTTP/2 multiplexes all 12 streams over one TCP connection; TCP delivers bytes in order, so the lost packet holds back every stream on that connection (100%) until the retransmission arrives, which costs at least about one RTT (~100 ms) and much more if recovery needs a retransmission timeout. HTTP/3 runs each stream independently over QUIC, so only the stream whose packet was lost waits (about 1/12 of the streams here). This is why HTTP/2 can lose to several HTTP/1.1 connections on lossy links, while its single connection and header compression still win on clean ones.
REST vs RPC
Both are ways to design an HTTP API — they differ in what the URL and verb are supposed to represent.
REST
URLs represent resources (/users/42), and HTTP methods represent the operation on that resource (GET = read, POST = create, PUT/PATCH = update, DELETE = remove). Status codes and headers carry semantic meaning. Because reads usually use GET, REST-style APIs can leverage HTTP caching semantics when responses send appropriate cache headers.
RPC (gRPC, JSON-RPC)
URLs or methods represent actions (/createUser, /sendEmail) rather than resources. Many RPC APIs send most calls as POST regardless of whether they read or write, and pair that with strongly typed schemas such as protobuf. The trade-off is that you get strict contracts and efficient tooling, but use less of HTTP's native caching and uniform-interface semantics.
Common Mistakes
The same handful of mistakes show up across almost every HTTP-based system.
FAQs
Is HTTP/2 always faster than HTTP/1.1?
Usually, but not universally — on a very low-latency, low-loss network the difference can be negligible, and HTTP/2's single-connection design can occasionally underperform multiple HTTP/1.1 connections if packet loss or implementation quirks dominate. In practice, for typical internet conditions with many small resources, HTTP/2 is often a clear win.
Why doesn't every site use HTTP/3 yet?
QUIC/UDP is sometimes blocked or throttled by corporate firewalls and some ISPs, and server-side QUIC support is newer/less battle-tested than mature TCP stacks. Most CDNs support it as an option with automatic fallback to HTTP/2.
Do I need cookies if I use JWTs?
No. A JWT is a token format; a cookie is one way to store and send that token. In browser apps, an HttpOnly cookie is often safer than exposing the token to JavaScript, while non-browser clients commonly send Authorization: Bearer <token> directly.
What's the difference between a reverse proxy and an API gateway?
An API gateway is typically a reverse proxy with API-specific features layered on top — request/response transformation, auth policy, rate limiting per API key, and sometimes request aggregation. See API Gateway fundamentals for the fuller boundary, and Reverse Proxy fundamentals for the overlap.
Real-World Architectures
Three shapes cover most production HTTP topologies you'll encounter.
Static site + CDN
A pre-built HTML/JS/CSS bundle served mostly from a CDN edge, with the origin touched primarily on cache misses or deploy-time updates. Extremely cheap and fast on the request path because the CDN absorbs most traffic spikes.
Load-balanced app tier + managed database
The classic shape: CDN for static assets → load balancer → N stateless app servers → a managed database (with read replicas) behind it. Scaling the app tier is just adding instances; the database is the harder scaling problem, usually solved with caching (Redis) in front of it.
Microservices behind an API gateway
One public entrypoint (the gateway) fans out internally to dozens of services, often communicating via gRPC/RPC internally while presenting a REST-ish public API. Distributed tracing becomes essential once a single user request can fan out across 10+ internal hops.
- Statelessness is what makes horizontal scaling and failover trivial for HTTP servers.
- HTTP/2 fixed app-level head-of-line blocking; HTTP/3 fixed the remaining transport-level blocking.
- REST leans on HTTP's native caching and semantics; RPC treats HTTP as a fast, typed transport.
- Most production incidents trace back to unsafe GETs, missing cache versioning, or retries without backoff.
- An API gateway is a reverse proxy with API-aware features (auth, rate limiting, transformation) added on top.