HTTP — HyperText Transfer Protocol
HTTP is the application-layer protocol that powers the web. It is request-response, stateless, and text-based (in HTTP/1.1). Understanding HTTP methods, status codes, headers, and the evolution from HTTP/1.1 to HTTP/2 to HTTP/3 is fundamental to every web system design.
How it works
HTTP is a request-response protocol: a client sends a request, the server sends a response. It is stateless — each request is independent, and the server doesn't remember previous requests (unless you add cookies or sessions).
This statelessness is both a strength and a weakness: it makes HTTP easy to scale (any server can handle any request) but means the application must manage state through other means (cookies, tokens, server-side sessions).
HTTP methods and their semantics:
| Method | Safe? | Idempotent? | Purpose |
|---|---|---|---|
| GET | Yes | Yes | Fetch a resource. No side effects. Cacheable. |
| POST | No | No | Create a resource. Side effects, not idempotent. |
| PUT | No | Yes | Replace a resource. Same request twice = same state. |
| DELETE | No | Yes | Remove a resource. Second delete returns 404 but state is same. |
| PATCH | No | Maybe | Partially update. Idempotent if it's 'set to X', not if it's 'increment by 1'. |
Safe methods have no side effects — calling them doesn't change server state. They can be cached and prefetched.
Idempotent methods can be safely retried — doing them once or 100 times produces the same result. This matters for unreliable networks: if a request times out, you can safely retry idempotent methods but not non-idempotent ones.
If a network blip causes your payment request to time out, should you retry? If the method is idempotent (PUT, DELETE), yes — retrying is safe. If it's POST (not idempotent), retrying might charge the user twice. This is why payment APIs use idempotency keys: the client sends a unique ID with each request, and the server deduplicates. Stripe, Square, and Adyen all use this pattern. Without it, retries would be unsafe.
HTTP status codes tell the client what happened:
- 2xx Success: 200 OK, 201 Created, 204 No Content
- 3xx Redirect: 301 Moved Permanently, 304 Not Modified (cache hit — don't re-download)
- 4xx Client error: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
- 5xx Server error: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout
In system design, 429 (rate limited) and 503 (unavailable) are especially important — they signal the client to back off or retry. Always include a Retry-After header with 429 so the client knows how long to wait.
Common mistakes:
- Returning 200 OK with an error body. This breaks HTTP semantics and client error handling. Use 4xx/5xx.
- Returning 500 for client errors (bad input). Use 400.
- Not returning 201 Created when a resource is created.
HTTP version evolution:
HTTP/1.1 (1997): text-based protocol. One request per TCP connection (later, pipelining was added but suffered from head-of-line blocking). Headers sent as plain text on every request (cookies can be several KB). Still the most widely deployed version.
HTTP/2 (2015): binary framing. Multiple requests multiplexed over a single TCP connection — no head-of-line blocking at the HTTP level. Header compression (HPACK) reduces overhead. Server push (deprecated in practice). Adopted by most modern APIs.
HTTP/3 (2022): runs over QUIC (UDP-based). Eliminates TCP head-of-line blocking entirely — if one stream is slow, others aren't blocked. Faster connection setup (0-RTT — can send data on the first packet). Better on mobile networks (survives IP changes). Default in Chrome and Cloudflare.
The semantics (methods, status codes, headers) are the same across versions — only the transport changes. HTTP/3 is backward-compatible with HTTP/2 and HTTP/1.1 at the application layer.
Key HTTP headers for system design:
Caching:
Cache-Control: max-age=60— cache for 60 seconds.ETag: "abc123"— content hash. Client sendsIf-None-Match: 'abc123'— if unchanged, server returns 304 Not Modified (saves bandwidth).Last-Modified/If-Modified-Since— timestamp-based caching.
Performance:
Connection: keep-alive— reuse TCP connection (HTTP/1.1 default).Accept-Encoding: gzip— compress response.Transfer-Encoding: chunked— stream response without knowing total size.
Security:
Authorization: Bearer {token}— auth token.Strict-Transport-Security— force HTTPS.Content-Security-Policy— prevent XSS.
Rate limiting:
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset— tell client their quota.Retry-After: 30— wait 30 seconds before retrying (sent with 429).
Your payment API receives a POST /charges request, but the network times out before you get a response. The user might have been charged, or might not have. What should you do?
Pick one answer.
What is the main performance improvement of HTTP/2 over HTTP/1.1?
Pick one answer.
Which HTTP status code should you return when a client has sent too many requests and is being rate-limited?
Pick one answer.
HTTP/1.1 vs HTTP/2 vs HTTP/3 — side by side.
| Property | HTTP/1.1 (1997) | HTTP/2 (2015) | HTTP/3 (2022) |
|---|---|---|---|
| Transport | TCP | TCP | QUIC (UDP) |
| Multiplexing | none (1 req per conn, or pipelining with HOL blocking) | yes — parallel streams over 1 TCP conn | yes — independent streams over QUIC |
| Head-of-line blocking | HTTP-level (pipelining) + TCP-level | TCP-level only (one lost packet stalls all streams) | none — streams are independent |
| Connection setup | TCP handshake (1 RTT) + TLS (1-2 RTT) = 2-3 RTT | same: TCP + TLS = 2-3 RTT | 1 RTT (or 0-RTT with resumed session) |
| Header encoding | plain text, sent every request | HPACK compression | QPACK compression (HPACK-like, stream-aware) |
| Server push | no | yes (deprecated in practice) | no |
| Connection migration | no — new IP breaks the connection | no — same as HTTP/1.1 | yes — QUIC connection ID survives IP changes |
| Deployment ubiquity | universal | ~98% of websites | ~30% and growing (Chrome, Cloudflare, Facebook) |
The semantics (methods, status codes, headers) are unchanged across all three versions — only the transport and framing change. This is by design: HTTP/3 is fully backward-compatible with HTTP/2 at the application layer.
Idempotency keys in practice. Stripe popularized the pattern: every POST request includes an Idempotency-Key header (a client-generated UUID). The server treats any two requests with the same key as the same logical operation — the second one returns the cached result of the first instead of re-processing.
The flow is:
- Client generates a UUID for this logical operation (e.g., 'charge this user $50 for order 1234').
- Client sends
POST /v1/chargeswithIdempotency-Key: <uuid>. - Server checks the idempotency store (Redis, DynamoDB, or a DB table): is this key present?
- If yes → return the stored response.
- If no → process the request, store the response keyed by the UUID with a TTL (e.g., 24h), return the response.
- If the network fails before the client receives the response, the client retries with the same UUID. The server returns the stored response (or returns 'still processing' if the original is in flight).
This converts a non-idempotent POST into a safely-retryable operation. Without it, a network blip during a payment could double-charge the user, or worse, partially charge and partially refund — leaving the system in an ambiguous state that requires manual reconciliation.
The same pattern applies to webhook delivery, background job processing, and any 'do this exactly once' API. The catch: 'exactly once' is impossible in distributed systems; what you really get is 'at-least-once delivery with at-most-once side effect via deduplication.'
Stripe's API requires (and the SDK auto-generates) an Idempotency-Key header for every POST that has side effects (charges, refunds, transfers). The Stripe SDK stores the key locally and reuses it across retries with exponential backoff. This is what lets Stripe tell its customers 'retry safely on any network error' — they could not say that without server-side deduplication. The keys live in Stripe's idempotency store for 24 hours; longer-lived deduplication requires a customer-supplied key (e.g., order ID) instead. This is the gold standard for any payment, billing, or order API. If you build such an API without idempotency keys, your support team will eventually reconcile double-charges by hand.
You're loading a page with 20 sub-resources over HTTP/2. The connection is on a flaky mobile network with 2% packet loss. Why will switching to HTTP/3 (QUIC over UDP) meaningfully improve page load time, when HTTP/2 already multiplexes?
Pick one answer.
The original request might have reached the server (and charged the user) just before the 504 — or it might never have reached the server. The client cannot tell. POST is not idempotent, so naive retries can multiply side effects.
Your mobile app calls POST /v1/charges to charge a user $50. The network returns a 504 Gateway Timeout. The user has spotty reception and taps 'Retry' twice. Without idempotency keys, what happens, and how do you fix it?
Engineering mental model
Mental model. Think of HTTP — HyperText Transfer Protocol as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does HTTP — HyperText Transfer Protocol mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing HTTP — HyperText Transfer Protocol, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
curl -i https://api.example.com/v1/http
# Look for:
# - status code
# - latency
# - retryability
# - response sizeBack-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: HTTP — HyperText Transfer Protocol
Change the variables below and predict what breaks first in HTTP — HyperText Transfer Protocol. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on HTTP — HyperText Transfer Protocol, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using HTTP — HyperText Transfer Protocol. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using HTTP — HyperText Transfer Protocol?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses HTTP — HyperText Transfer Protocol, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose HTTP — HyperText Transfer Protocol, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for HTTP - HyperText Transfer Protocol: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using HTTP - HyperText Transfer Protocol. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Stateless — easy to scale (any server handles any request).
- +Text-based (HTTP/1.1) — easy to debug with curl, telnet, browser dev tools.
- +Wide support — every language, every platform, every firewall allows it.
- +Caching built into the protocol (Cache-Control, ETag).
- −Request-response only — not suitable for server push (use WebSockets or SSE).
- −Text-based HTTP/1.1 is inefficient — headers sent as text every time.
- −Connection overhead — each TCP connection costs time (mitigated by HTTP/2 multiplexing).
- −Verbose — JSON payloads are larger than binary protocols (gRPC/Protobufs).
How this breaks in production
- Head-of-line blocking in HTTP/1.1 (a slow request blocks others on the same connection).
- Excessive header size — cookies can bloat requests to several KB.
- Connection exhaustion — too many concurrent connections overwhelm the server.
- Returning 200 OK for errors — breaks HTTP semantics and client error handling.
Don't fall into these traps
- •Using GET for operations with side effects (violates HTTP semantics, breaks caching).
- •Treating POST as idempotent (it's not — retries can duplicate).
- •Returning 200 for errors — use proper 4xx/5xx codes so clients can handle them correctly.
- •Not including Retry-After with 429 responses.
Real systems using this
How real systems implement this
- Stripe API — Uses idempotency keys for all POST requests so clients can safely retry payment operations without double-charging. Documented in their API guide.
- Cloudflare — Serves HTTP/3 to all clients by default, reducing connection latency globally. Also uses HTTP/2 Server Push (though this is being deprecated).
Practice saying it out loud
- Q1What's the difference between HTTP/1.1, HTTP/2, and HTTP/3?
- Q2Which HTTP methods are idempotent, and why does it matter?
- Q3How would you design an API to be safely retryable?
- Q4What status codes do you use for rate limiting? What header should you include?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
TCP — Transmission Control Protocol