Guide

HTTP is the protocol you will use most in your career. Almost every public API, most internal service calls, every browser page load, and gRPC between microservices ride on it. HTTP has two parts, and keeping them apart is half of understanding it.

The first part is semantics: methods, status codes, headers, caching rules and cookies. These have barely changed since the 1990s, and RFC 9110 defines them once for every version. The second part is the wire format, which is how messages are laid out as bytes on a connection. That is what changed: HTTP/1.1 uses text over TCP, HTTP/2 uses binary frames multiplexed over one TCP connection, and HTTP/3 uses frames over QUIC on UDP. Each new wire format was built to fix the latency problem of the one before.

Concepts in this section

Start with the first page. It covers the vocabulary the rest of the section uses.

StepConceptWhat it answers
1Requests, methods and status codesWhich method and status code fit this endpoint?
2HTTP/1.1 and HTTP/2 multiplexingWhy did browsers open six connections, and what did HTTP/2 change?
3HTTP/3 and QUICHow does QUIC remove the stall that TCP still causes?
4HTTP cachingHow do Cache-Control and ETag avoid repeat work?
5Cookies, CORS and streamingHow does browser state work, and when do responses stream?

Comparing the three versions

HTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC over UDP
FormatTextBinary framesBinary frames
ConcurrencyOne request at a time per connection (6 connections per host)Many streams on one connectionMany streams on one connection
HTTP-level head-of-line blockingYesNoNo
Transport-level head-of-line blockingPer connectionYes, across all streamsNo, per stream only
Header compressionNoneHPACKQPACK
New connection setup with TLS 1.32 RTT (TCP plus TLS)2 RTT1 RTT, or 0-RTT on resumption
Survives network changeNoNoYes (connection IDs)
Best forSimple clients, debugging, internal trafficMost server-to-server and gRPCMobile and lossy, long-distance users

Common mistakes

  • Using a 200 response with an error body. Clients, retries, caches and monitoring all read the status code. A 200 with {"error": ...} breaks all four.
  • Retrying every failed request. Retry only idempotent requests automatically, and add an idempotency key to any POST that moves money or creates things.
  • Setting cache headers by default. A wrong public on a personalized page can leak one user's data to another.
  • Mismatched keep-alive timeouts. If the app closes idle connections before the load balancer does, the load balancer reuses dead connections and you see sporadic 502s. Make the app's keep-alive timeout longer.

Key takeaways

  • HTTP semantics (methods, status codes, headers and caching) are shared by all versions. Only the wire format changed.
  • Safe and idempotent are the method properties that decide what can be retried and cached.
  • HTTP/1.1 handles one request at a time per connection. HTTP/2 multiplexes streams over one TCP connection, but TCP still stalls every stream on loss.
  • HTTP/3 runs on QUIC, which recovers loss per stream, merges transport and TLS setup, and survives network changes.

Further learning

ByteByteGo's short animated overview shows how each version fixed the previous one's bottleneck. It is a good visual summary of the table above.

HTTP/1 to HTTP/2 to HTTP/3ByteByteGo

MDN: HTTP is the reference for headers and status codes.

High Performance Browser Networking (Ilya Grigorik) covers the transport layer under HTTP in depth.