HTTP (1.1 / 2 / 3)
The application protocol of the web — from 1.1 to 3
HTTP/1.1 — The Foundation
HTTP (Hypertext Transfer Protocol) is a request-response protocol operating at Layer 7. The client sends a request, the server returns a response. Every interaction on the web — loading a page, submitting a form, calling an API — is an HTTP exchange. HTTP/1.1 (RFC 2616, later refined in RFCs 7230-7235) has been the workhorse since 1997.
Request Format
An HTTP/1.1 request is a text-based message with a well-defined structure:
GET /api/v1/pods HTTP/1.1
Host: kubernetes.default.svc
Authorization: Bearer eyJhbGciOi...
Accept: application/json
Connection: keep-aliveResponse Format
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1842
Cache-Control: no-cache
ETag: "a1b2c3d4"
{"kind":"PodList","apiVersion":"v1","items":[...]}Request / Response Flow
Note
Note: In HTTP/1.1 without pipelining, each request must wait for the previous response before being sent. This is inherently sequential on a single connection.
HTTP Methods
| Method | Semantics | Idempotent | Has Body | Typical Use |
|---|---|---|---|---|
GET |
Retrieve resource | Yes | No | Fetch data, load pages |
POST |
Create / submit | No | Yes | Create resource, form submit |
PUT |
Replace entire resource | Yes | Yes | Full update (replace) |
PATCH |
Partial update | No* | Yes | Update specific fields |
DELETE |
Remove resource | Yes | Optional | Delete resource |
HEAD |
GET without body | Yes | No | Check if resource exists, get headers |
OPTIONS |
Describe capabilities | Yes | No | CORS preflight, API discovery |
Note
*PATCH idempotency: PATCH is not guaranteed idempotent because the operation depends on the patch format. A JSON Merge Patch ({"name":"new"}) is idempotent, but a JSON Patch ([{"op":"add","path":"/tags/-","value":"x"}]) that appends to an array is not.
Key Headers
Content Negotiation & Identity
Host— Required in HTTP/1.1. Identifies the target virtual host. One IP can serve many domains (name-based vhosts in nginx/Apache, Ingress in K8s).Content-Type— MIME type of the body:application/json,text/html,multipart/form-dataAccept— What the client can handle:application/json, text/html;q=0.9(quality factor for preference)Authorization— Credentials:Bearer <token>,Basic <base64>
Caching
Cache-Control— Directives for caches:max-age=3600— fresh for 3600sno-cache— must revalidate before use (can still cache)no-store— do not cache at allpublic/private— CDN vs browser-only
ETag— Opaque identifier for a specific version of a resource (like a content hash)If-None-Match— Client sends previous ETag; server returns304 Not Modifiedif unchanged
Connection Management
Connection: keep-alive— Default in HTTP/1.1. Reuse the TCP connection for multiple requests instead of opening a new one per request (which was the HTTP/1.0 default). SendConnection: closeto indicate the connection will be closed after this response.
Conditional Request Flow (ETag)
Tip
Ops insight: ETags save bandwidth and reduce origin load. In K8s, the API server uses resourceVersion as an ETag-like mechanism for watch and list operations — same principle.
Status Codes
| Range | Category | Key Codes |
|---|---|---|
1xx |
Informational | 100 Continue — server ready for body; 101 Switching Protocols — WebSocket upgrade |
2xx |
Success | 200 OK; 201 Created (POST success); 204 No Content (DELETE success, no body) |
3xx |
Redirection | 301 Moved Permanently (update bookmarks); 302 Found (temporary redirect); 304 Not Modified (cache hit); 307 Temporary Redirect (preserve method); 308 Permanent Redirect (preserve method) |
4xx |
Client Error | 400 Bad Request; 401 Unauthorized (not authenticated); 403 Forbidden (authenticated but not authorized); 404 Not Found; 405 Method Not Allowed; 409 Conflict; 429 Too Many Requests (rate limited) |
5xx |
Server Error | 500 Internal Server Error; 502 Bad Gateway (upstream failed); 503 Service Unavailable (overloaded/maintenance); 504 Gateway Timeout (upstream timeout) |
Warning
Ops war story: 502 vs 503 vs 504 — know the difference cold. 502 means your reverse proxy (nginx, envoy, ALB) got a bad response from upstream. 503 means the service itself is refusing connections. 504 means the upstream didn't respond in time. These point to very different root causes.
Keep-Alive (Persistent Connections)
- New TCP connection per request
- 3-way handshake overhead every time
- TCP slow start penalty on every connection
- Massive latency cost
Request 1: TCP open → GET → response → TCP close
Request 2: TCP open → GET → response → TCP close
Request 3: TCP open → GET → response → TCP close- Reuse TCP connection for multiple requests
- One handshake, many requests
- TCP congestion window grows over time
- Connection stays warm
TCP open → GET → response
→ GET → response
→ GET → response
→ ... → TCP closePipelining — A Good Idea That Failed
HTTP/1.1 pipelining allows the client to send multiple requests on a single connection without waiting for each response. In theory, this eliminates the round-trip latency between sequential requests.
Warning
Head-of-Line (HOL) Blocking: The server MUST respond in the order requests were received. If /a takes 5 seconds, /b and /c sit waiting even if they're ready — hence "head of line" blocking. Most browsers and proxies never enabled pipelining because of this, plus broken intermediaries (proxies that corrupted pipelined responses). The real-world workaround was opening 6-8 parallel TCP connections per domain.
HTTP/2 — Multiplexing Over a Single Connection
HTTP/2 (RFC 7540, 2015) was designed specifically to solve HTTP/1.1's performance problems: HOL blocking, redundant headers, and the hack of opening multiple connections. It keeps HTTP semantics (methods, status codes, headers) identical but completely replaces the wire format.
Binary Framing Layer
GET /api/pods HTTP/1.1\r\n
Host: k8s.io\r\n
Accept: application/json\r\n
\r\n- Human-readable text
- Parsed by scanning for delimiters (
\r\n) - Headers are verbose, repeated every request
- Machine-optimized binary format
- Fixed-length frame headers — fast parsing
- Every frame tagged with a stream ID
Multiplexed Streams
The core innovation: multiple streams (logical request/response pairs) are interleaved on a single TCP connection. Each stream has a unique ID. Frames from different streams can be sent and received in any order.
HTTP/2 Multiplexing — Single TCP Connection
Time ──────────────────────────────────────────▶
Stream 1 |HEADERS||DATA| |DATA||END|
Stream 3 |HEADERS||DATA|DATA||END|
Stream 5 |HEADERS| |DATA||END|
All frames interleaved on ONE TCP connection.
No HTTP-layer HOL blocking — stream 3 can finish
before stream 1 without waiting.Tip
Ops insight: This is why HTTP/2 made domain sharding obsolete. With HTTP/1.1, sites served assets from multiple domains (cdn1.example.com, cdn2.example.com) to open more parallel connections. HTTP/2 multiplexing on a single connection outperforms that hack.
HPACK Header Compression
HTTP/1.1 headers are sent as uncompressed text on every request. For a typical page with 80+ resources, the same headers (User-Agent, Cookie, Accept, etc.) are sent 80+ times — often >1 KB per request just in headers.
HPACK Compression Strategy
Static Table
61 pre-defined common header name-value pairs (e.g., :method: GET = index 2). Referenced by index instead of sending the full string.
Dynamic Table
Connection-specific table built during the session. Headers seen before are added and referenced by index on subsequent requests. FIFO eviction.
Huffman Encoding
Header values that can't be indexed are Huffman-coded — a static Huffman table optimized for HTTP header byte frequencies. ~30% size reduction.
Note
Result: HPACK typically achieves 85-95% compression on repeated headers. The first request sends full headers; subsequent requests on the same connection send tiny index references.
Server Push
The server can proactively send resources to the client before the client requests them. For example, when the client requests index.html, the server can push style.css and app.js it knows the client will need.
Warning
Server push is being deprecated. In practice, it's extremely hard to use correctly: you can push resources the client already has cached (wasting bandwidth), the cache interaction semantics are complex, and CDNs/proxies often break it. Chrome removed support in 2022. The replacement pattern is 103 Early Hints — the server sends a 103 informational response with Link headers so the browser can start fetching resources early, but the client controls the actual requests.
Stream Priorities
HTTP/2 allows clients to assign priorities to streams using a dependency tree and weight values (1-256). This lets the browser signal that CSS should load before images, for example. In practice, implementations varied widely — both browsers and servers often handled priorities poorly, leading to the RFC 9218 (Extensible Priorities) overhaul.
HTTP/3 — QUIC and the End of TCP for HTTP
HTTP/2 solved HTTP-layer HOL blocking, but one problem remained: TCP-layer HOL blocking. Because HTTP/2 multiplexes all streams over a single TCP connection, a single lost TCP packet blocks ALL streams until retransmission completes — the TCP layer doesn't know about HTTP streams. HTTP/3 (RFC 9114, 2022) replaces TCP with QUIC (RFC 9000), a UDP-based transport that solves this fundamentally.
The TCP HOL Blocking Problem
Stream 1: |HEADER|DATA|
Stream 3: |HEADER|DATA|DATA|
Stream 5: |HEADER|DATA|
↑
TCP packet lost here
ALL streams blocked until
retransmission arrivesTCP is a single ordered byte stream. It doesn't understand multiplexed streams. Lost segment = everything waits.
Stream 1: |HEADER|DATA|
Stream 3: |HEADER| ✗ |DATA|
Stream 5: |HEADER|DATA| ✓
↑
QUIC packet lost on stream 3
Only stream 3 is blocked
Streams 1 & 5 continueQUIC has native stream awareness. Loss on one stream only blocks that stream. Others proceed independently.
QUIC Key Features
0-RTT Connection Establishment
TCP + TLS 1.3 requires 2-3 round trips before data can flow (1 RTT TCP handshake + 1 RTT TLS handshake). QUIC integrates transport and TLS into a single handshake:
- First connection: 1-RTT (transport + crypto combined)
- Resumed connection: 0-RTT — client sends data immediately using a PSK from a previous session
Connection Migration
TCP connections are identified by the 4-tuple: (src IP, src port, dst IP, dst port). If your IP changes (e.g., switching from WiFi to cellular), the TCP connection breaks and must be re-established.
QUIC connections are identified by a Connection ID — an opaque token independent of the IP/port. When the network changes, the connection survives.
Built-in TLS 1.3
QUIC does not run over a separate TLS layer — TLS 1.3 is embedded into the protocol. All QUIC payloads (including most header fields) are encrypted. There is no unencrypted QUIC — unlike TCP where encryption is optional. This means middleboxes (firewalls, NATs, DPI devices) cannot inspect or modify QUIC headers, which has both security benefits and operational challenges.
Warning
Ops note: Because QUIC runs over UDP, some corporate firewalls and network middleboxes block or rate-limit UDP/443. Browsers implement QUIC with a fallback: try QUIC first, fall back to HTTP/2 over TCP if it fails. You'll see the Alt-Svc header in HTTP/2 responses advertising QUIC support: Alt-Svc: h3=":443"; ma=86400.
HTTP/1.1 vs HTTP/2 vs HTTP/3 — Side by Side
| Feature | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Transport | TCP | TCP | QUIC (UDP) |
| Wire format | Text | Binary frames | Binary frames |
| Multiplexing | No (1 req at a time per conn) | Yes (many streams, 1 conn) | Yes (many streams, 1 conn) |
| HOL blocking | HTTP + TCP layer | TCP layer only | None |
| Header compression | None | HPACK | QPACK |
| Server push | No | Yes (deprecated) | Yes (rarely used) |
| Connection setup | 1-RTT TCP + 1-RTT TLS | 1-RTT TCP + 1-RTT TLS | 1-RTT (0-RTT on resume) |
| Connection migration | No | No | Yes (Connection ID) |
| Encryption | Optional (HTTPS) | Effectively required | Mandatory (built-in TLS 1.3) |
| RFC | 7230-7235 | 7540, 9113 | 9114 (QUIC: 9000) |
Connection Setup Comparison
HTTP/1.1 + TLS 1.2 (3 RTTs)
- TCP SYN
- TCP SYN-ACK
- TCP ACK + TLS ClientHello
- TLS ServerHello + Cert
- TLS Key Exchange + Finished
- HTTP Request
3 RTTs before first byte of data
HTTP/2 + TLS 1.3 (2 RTTs)
- TCP SYN
- TCP SYN-ACK
- TCP ACK + TLS ClientHello
- TLS ServerHello + Finished
- HTTP Request
2 RTTs before first byte of data
HTTP/3 + QUIC (1 RTT / 0 RTT)
- QUIC Initial (ClientHello + transport params)
- QUIC Handshake (ServerHello + Finished)
- HTTP Request
1 RTT (0 RTT on resumption!)
Tip
Real-world impact: On a 100ms RTT connection (common for cross-region traffic), the difference between 3 RTTs (300ms) and 1 RTT (100ms) is huge — especially for mobile users on high-latency cellular networks. 0-RTT on resume means returning users get data flowing with their first packet.
Note
Checking protocol in production: Use curl -I --http2 https://example.com to check HTTP/2 support. For HTTP/3: curl --http3 https://example.com (requires curl built with HTTP/3 support). In Chrome DevTools, the Network tab's "Protocol" column shows h2 or h3.