SwadhyAI Notes

All System Design notes

System Design Networking

HTTP Deep Dive

You open ChatGPT, type a message, and watch the answer stream token by token. Or you refresh Instagram and a dozen images start loading together. Both stories speak : request in, response out, even when the wires underneath change.

, , and mostly fight wasted , , and fat headers while keeping familiar methods and status codes.

What each generation fixes

1

Keep-alive helps, but one queue still waits per connection.

2

Many streams plus compressed headers share one TCP pipe.

3

Same HTTP meaning on QUIC over UDP for lossy paths.

4

Encrypt the channel; newer TLS starts with fewer waits.

What actually happens

is the usual language of browsers calling APIs and servers returning HTML or JSON. Each interaction is a request (method, path, headers, optional body) and a response (status code, headers, body). adds so that conversation stays private.

Moving from HTTP/1.1 to HTTP/2 to HTTP/3 typically improves , header efficiency, and handshake latency, especially on lossy or mobile networks. Your REST resource naming usually stays the same; the transport behavior changes. Setup timing and capacity math live in Numbers to know.

Before the wire-format details, decide which version your edge should offer and when HTTP/3 is worth enabling.

How to choose an HTTP version

1. What do your clients actually negotiate? Enable newer versions at the CDN or edge first. Clients fall back through or .

2. Is the pain many small resources or packet loss? helps page loads with many assets. helps when mobile loss or handshake latency dominates.

3. Do you need universal tooling? Internal microservices and debugging still lean on HTTP/1.1 simplicity; public SPAs and gRPC often default to HTTP/2.

Simple rule of thumb

Use as the default for web clients and many APIs. Enable on the CDN/edge for mobile users. Do not pitch HTTP/3 as a rewrite of your API: methods and status codes stay the same. Exact setup floors live in Numbers to know.

HTTP semantics vs transport

Semantics stay stable

GET, POST, status codes, cache headers, and content negotiation still mean the same thing across versions.

Transport changes

HTTP/2 adds multiplexed streams over TCP. HTTP/3 uses QUIC over UDP to reduce setup and packet-loss stalls.

0-RTT caution

0-RTT is useful for resumed safe requests, but avoid it for unsafe operations that could be replayed.

Payload and bandwidth budget

Header overhead

Assume each request carries 2 KB of headers and the service receives 50,000 requests per second. Multiplying bytes per request by requests per second gives 100 MB/s of request headers alone, using decimal units. This excludes response bodies and lower-level overhead. Reducing a cookie helps repeatedly because the browser may attach it to every matching request.

Field trimming

8KB response trimmed to 800B saves 7.2KB. Across 1B reads, that is about 7.2TB less transfer.

Compression

100KB JSON compressed to 20KB saves 80KB. Across 10M responses, that is about 800GB less egress.

Fan-out

One 600B event to 10K subscribers becomes 6MB of outbound payload, before protocol and encryption overhead.

With the choice framed, a short history shows why each generation existed without changing what GET or POST means.

HTTP: tested over decades

HTTP has been refined across 30+ years of real-world internet traffic. Each version solved specific pain points while keeping backward compatibility.

1991

HTTP/0.9

GET only, no headers

→

1996

HTTP/1.0

Headers, status codes

→

1997

HTTP/1.1

Keep-alive, chunked

→

2015

HTTP/2

Multiplexing, binary

→

2022

HTTP/3

QUIC, 0-RTT

Why HTTP won: simplicity (easy to debug), universality (every browser, proxy, and CDN speaks it), and extensibility (headers add features without breaking old clients).

Next, read a URL the way the stack does: scheme, host, path, query, then the verb that says what to do.

URL anatomy and HTTP verbs

Every part of the URL means something

A URL is not just a string: every segment tells the server what you want, where it lives, and how to find it.

https://api.example.com:443/v2/users/123?fields=name,email#section1

Scheme

https: protocol to use

Host

api.example.com: DNS resolves this to an IP

Port

:443: default for HTTPS (80 for HTTP)

Path

/v2/users/123: resource location

Query

?fields=name,email: parameters/filters

Fragment

#section1: client-side only, never sent to server

HTTP verbs: what you want to do

Safe methods are intended for retrieval without a requested business-state change; incidental logging is still possible. Idempotent methods have the same intended effect when repeated as when applied once. A second DELETE can return a different response while the resource remains deleted, so identical responses are not required. POST is not generally idempotent, but an application can add an idempotency contract for a particular operation.

The method (verb) tells the server what action to perform on the resource. It goes in the first line of the request:

VerbActionHas body?Example
GETRead / fetch resourceNoGET /users/123
POSTCreate new resourceYesPOST /users
PUTReplace entire resourceYesPUT /users/123
PATCHPartial updateYesPATCH /users/123
DELETERemove resourceNoDELETE /users/123
HEADGET but no body (headers only)NoHEAD /users/123
OPTIONSWhat methods are allowed?NoOPTIONS /users

HTTP status codes that matter

ClassMeaningExamples
2xx SuccessRequest handled as intended.200 OK, 201 Created, 204 No Content
3xx RedirectLook elsewhere (or use cached).301/302 redirect, 304 Not Modified
4xx Client errorFix the request, auth, or rate of calling.400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xx Server errorServer or upstream failed; client may retry carefully.500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout

429 Too Many Requests

Tell clients they hit a quota. Pair with Retry-After when you can. At the edge, returning 429 is a standard control for abuse and noisy neighbors.

Simple rule of thumb

Pick codes that match ownership: bad input → 4xx; overloaded dependency → 503; auth missing vs forbidden → 401 vs 403. Consistent error bodies plus status codes beat inventing string-only failures.

Status codes answer outcome. The request and response lines below show where methods, headers, and bodies sit on the wire.

Request, response, and negotiation

Request and response anatomy

The method describes the requested operation, the target identifies the resource, and headers carry metadata about representation, authorization, and intermediaries. A status code describes the outcome at the HTTP boundary; the body contains the representation or error details. This separation lets caches and proxies make useful decisions without understanding every application field, provided the application uses the semantics consistently.

Every HTTP interaction is a request (what the client wants) and a response (what the server sends back). Headers carry metadata about the message itself.

HTTP request

http
GET /api/v2/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJI...
Accept-Encoding: gzip, br
User-Agent: MyApp/2.1
Cache-Control: no-cache

Request line: method, path, HTTP version

Host: which server (required in HTTP/1.1; enables virtual hosting)

Accept: what format I understand

Authorization: who I am

HTTP response

http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 245
Cache-Control: max-age=3600
ETag: "abc123def"
X-Request-Id: req_8f3k2j

{ "id": 123, "name": "Alice", "email": "a@b.com" }

Status line: version, status code, reason phrase

Content-Type: response format

Cache-Control: how long to cache

: fingerprint for conditional requests

Headers describe content, control caching, handle authentication, and enable features without changing the URL or body. Common groups: Authorization / Cookie;Cache-Control / ETag;Content-Type / Content-Length;Accept-Encoding / Content-Encoding.

Client and server agree on the response format through headers: the same URL can return JSON, HTML, or XML depending on what the client asks for.

Client says: here is what I understand

http
Accept: application/json
Accept: text/html
Accept: application/xml, application/json;q=0.9
Accept-Language: en-US, hi;q=0.8
Accept-Encoding: gzip, br

Server says: here is what I am sending

http
Content-Type: application/json; charset=utf-8
Content-Language: en-US
Content-Encoding: br

GET /users/123 with Accept: application/json may return {"id":123,"name":"Alice"}; a browser asking for text/html gets markup instead. Clients advertise decompressors; servers pick one and set Content-Encoding. Older clients that omit brotli still work. Compression is content negotiation: cache keys and CDNs must treat differently encoded bodies as distinct variants when appropriate.

Headers, HTTPS, and trust boundaries

Headers are flexible

HTTP header fields let a message carry metadata in addition to its body. An endpoint generally ignores a field it does not understand, but proxy forwarding and removal follow protocol rules and local configuration. Do not assume a custom identity or tracing field survives every intermediary. Content negotiation via Accept and Accept-Encoding is the classic lesson: the same URL can return different encodings or formats.

HTTPS = HTTP over TLS

TLS encrypts bytes in transit and authenticates the server (and optionally the client). Prefer HTTPS everywhere on the public internet; still validate tokens and permissions inside the app: encryption is not authorization.

Never trust the body for authz

Do not authorize with a userId from the JSON body alone. Derive the caller from the auth context (verified session, JWT subject, mTLS identity) and check that identity may act on the resource.

Authz and trust aside, the three HTTP generations differ mainly in how connections, streams, and loss recovery work.

Protocol generations side by side

: the workhorse

One request at a time per connection unless you open many parallel TCP pipes; simple but easy to leave bandwidth idle.

  • Keep-alive reuses one TCP socket so later requests skip a fresh handshake.
  • Pipelining is often disabled because FIFO responses still serialize wait time.
  • Chunked transfer streams the body so large replies need not be fully buffered first.
  • lets one slow response stall later replies; browsers open ~6 connections per domain.
  • Plain text headers repeat cookies and auth, so 1-2KB of bloat per request is common.

Cold setup cost is summarized in Numbers to know.

: multiplexed streams

Many logical streams ride one TCP session with compressed headers, though TCP itself can still block everything on one lost packet.

  • interleaves streams on one TCP connection so pages need fewer parallel sockets.
  • compresses repeated headers with tables and Huffman, often cutting 85–95% of header bytes.
  • Server Push was removed from Chrome in 2022; prefer 103 Early Hints for critical assets instead.
  • Stream prioritization uses weights and dependencies so browsers can prefer CSS and scripts over images.

: and UDP

QUIC rebuilds streams atop UDP so mobility, loss recovery, and TLS integrate without inheriting every TCP limitation.

  • QUIC runs over UDP with mandatory TLS 1.3 so encryption starts with the transport itself.
  • A connection ID survives IP change, which keeps mobile handoffs from restarting the session.
  • lets resumed clients send early data; weigh replay risk on non-idempotent writes.
  • Each QUIC stream recovers loss alone, so one lost packet does not freeze every stream.

Search, video, edge, and mobile fleets report better rebuffer, TTFB, and tail latency where UDP/443 is open; timing floors are in Numbers to know.

Version comparison

HTTP/2 interleaves frames from several streams on one TCP connection. This removes the application-level response ordering restriction of HTTP/1.1 pipelining, but TCP still withholds later bytes after a missing segment. HTTP/3 uses QUIC streams so a loss on one stream need not block delivery on another. Neither version removes database latency, shared congestion, or the need for application deadlines.

FeatureHTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
MultiplexingNoYesYes (no TCP HOL)
Header compressNoHPACKQPACK
HOL blockingHTTP + TCPTCP onlyNone at TCP layer
Connection setup1–3 RTTs1–2 RTTs0–1 RTTs

Fit to product: HTTP/1.1 for simple internal REST and curl debugging; HTTP/2 as the default for SPAs, gRPC, and gateways; HTTP/3 when mobile loss or handshake latency dominates (see How to choose).

Where QUIC fits in the story

QUIC provides reliable streams, congestion control, and encryption over UDP. HTTP/3 uses these streams to carry HTTP requests and responses. The useful distinction is between losing data within one stream and making unrelated streams wait for that same loss.

What it is

Reliable, shared, encrypted streams over UDP. HTTP/3 is normal HTTP meaning riding on QUIC instead of TCP.

Why it helps

Faster return visits, better behavior when a phone changes networks, and less freezing across unrelated downloads.

What not to do

Do not redesign everything around QUIC. Turn it on at the edge when useful, and keep most API design on plain HTTP semantics.

Interview answers often need the numeric floors behind those version rows. Keep the table in one place and cross-ref it.

Numbers to know

At 100ms , rough connection-setup floors before the application response (order of magnitude; your path may differ):

~300–400ms
HTTP/1.1 + TLS 1.2 cold
~200–300ms
HTTP/2 + TLS 1.3
~100–200ms
HTTP/3 new connection
~0–100ms
HTTP/3 resumed / 0-RTT
  • Cold TCP plus TLS 1.2 often costs about three RTTs before the first request byte.
  • Warm reuse or HTTP/3 / collapses setup toward roughly 0-1 RTT of wait.
  • At 50K QPS of short calls, saving ~200ms of setup is handshake capacity you keep.
  • HTTP/2 often tunes max concurrent streams around 100-128; validate your reverse proxy defaults.

Where time goes in one request

A user action is a chain of waits: find the name, open a connection, encrypt, send the request, do server work, download the response, then render.

Diagram text: the exact Mermaid definition below preserves the nodes, labels, and connections. Enable JavaScript to view the rendered diagram.
Read the diagram definition
sequenceDiagram
    autonumber
    participant Client
    participant DNS
    participant Edge as Edge or proxy
    participant App as Application tier
    participant Data as Cache or database

    Client->>DNS: Resolve host, often cached
    DNS-->>Client: Address from DNS records or traffic policy
    Client->>Edge: TCP 3-way handshake or QUIC setup
    Client->>Edge: TLS handshake and protocol negotiation
    Client->>Edge: Send headers and optional body
    Edge->>App: Reuse pooled upstream connection
    App->>Data: Read, write, or cache lookup
    Data-->>App: Result
    App-->>Edge: Status, headers, body
    Edge-->>Client: Compressed response bytes
    Client->>Client: Parse, execute, render, cache

Setup

A cold DNS lookup, TCP hello, and TLS check can burn one or more round trips before your app code even runs.

Bytes

Big cookies, fat headers, and large JSON bodies make upload and download slower. Compression and smaller payloads help.

Client world

Browsers add rules and limits. Phones add radio wake-up, loss, and network switches.

Cold start

DNS + TCP + TLS can easily cost a few hundred milliseconds on a 100ms path before useful request bytes finish setup.

Warm connection

A reused HTTPS connection can send immediately and skip most of that setup tax.

Header bloat

Large cookies and auth headers add cost on every request, even before the body starts.

Smaller bodies

If a 50 KB JSON response compresses to 10 KB, transfer size falls by 40 KB. The client still decompresses and parses the original JSON structure, so compression alone does not reduce JSON parsing work. Removing unneeded fields can reduce both transferred data and parsing effort.

Why reusing a connection feels so much faster

Saying hello once and keeping the pipe warm beats paying the hello tax on every request, especially when latency is high or you make many calls.

Without reuse

Twenty separate cold calls can burn seconds just on setup if they cannot overlap well.

With keep-alive

The first call pays setup. The next ones mostly pay for server work and moving the payload.

With multiplexing

Many requests can share one encrypted connection at once, which is how modern pages stay efficient.

Simple rule of thumb: if only the first request after idle is slow, look at DNS, TLS, cold pools, or mobile radio wake-up before blaming the database.

Lifecycle and reuse math set the stakes. Day-to-day knobs are TLS, streams, CDN negotiation, and compression.

Operations and compatibility

Caching is a reuse decision, while validation checks whether a stored response remains current. A fresh cache entry can be reused without contacting the origin. An expired entry may be revalidated with its ETag; an unchanged resource can return 304 and avoid retransmitting its body. “no-cache” requires validation before reuse, whereas “no-store” prohibits storing the response. Pick these directives based on the data’s visibility and freshness requirements.

Performance optimization tips

Origin / reverse proxy

  • Terminate with TLS 1.2+; prefer TLS 1.3 so the hello spends fewer round trips.
  • Enable HTTP/2 on 443 and tune max concurrent streams (often ~100–128; see Numbers to know).
  • Use push_preload only when traces show it wins; push can waste bytes on cache misses.
server {
  listen 443 ssl http2;
  ssl_protocols TLSv1.2 TLSv1.3;
  http2_max_concurrent_streams 128;
  http2_push_preload on;
}

CDN and edge

  • Turn on HTTP/3 where the vendor supports it; that is often the first hop users hit.
  • Set Cache-Control for static assets; separate cache keys for compressed variants.
  • Use Early Hints (103) or link preload to overlap RTTs with critical CSS and JS.
  • Serve Brotli on compressible text; keep images in modern formats at the edge.

Measure

  • Track protocol mix: HTTP/1.1 vs HTTP/2 vs HTTP/3 share by region and client.
  • Split TTFB into TLS handshake versus origin wait so you know which layer to fix.
  • Watch connection setup latency and QUIC 0-RTT eligibility against replay trade-offs.
  • Report cache hit ratio and bytes saved from compression to prove edge wins.

How HTTP stays backward compatible

Every new HTTP version can talk to older servers and clients. That is why the web did not break when HTTP/2 or HTTP/3 launched: billions of devices upgraded at their own pace.

  • during TLS lets the server pick the highest version both sides understand.
  • GET, POST, headers, and status codes stay the same; only framing and transport change.
  • Unknown headers are ignored, so new features stay opt-in without breaking old clients.
  • can advertise HTTP/3 after HTTP/2; if UDP is blocked, the client stays on TCP.

Enable HTTP/3 on the CDN edge gradually; old browsers fall back. The same API can serve HTTP/3 phones, HTTP/2 tablets, and HTTP/1.1 IoT without a flag day.

Debugging questions

  • Which protocol did the client actually negotiate on this request?
  • Is UDP/443 blocked so HTTP/3 users quietly fall back to HTTP/2?
  • Are large cookies or auth headers bloating bandwidth on every request?
  • Is tail latency due to TLS setup, origin wait, packet loss, or download size?
  • Are retries safe for this HTTP method and endpoint semantics?

Compatibility keeps old clients online. Close with on HTTP, the version invariant, a timing story, exercises, and references.

on HTTP

REST is not a protocol. It is an architectural style that maps CRUD operations to HTTP verbs and resources to URLs. It is the default way APIs are built because HTTP already provides everything REST needs.

HTTP featureREST uses it forExample
URLsResource identity/api/users/123
VerbsCRUD operationsGET, POST, PUT, DELETE
Status codesOperation results200 OK, 404 Not Found
HeadersAuth, caching, negotiationAuthorization, Cache-Control
BodyData payload{"name":"Alice"}
  • No new protocol is needed; existing CDNs, proxies, and caches already understand HTTP.
  • A GET URL is a natural cache key, so intermediary caches can serve repeats cheaply.
  • Requests stay human-readable in a browser or curl, which speeds debugging.
  • Stateless requests carry what they need, so you can scale app servers horizontally.
  • Pain points include over-fetching, under-fetching, endpoint explosion, and costly /v1 /v2 versioning.

Those pain points helped motivate GraphQL for some large mobile clients. Resource naming, idempotency, pagination, HATEOAS, and versioning live in the dedicated REST API Design note; this section only places REST inside HTTP's ecosystem.

Correctness invariant

What must never become false

HTTP semantics stay stable across versions: a safe method stays safe, an idempotent method stays idempotent, and intermediaries must not invent authority from client-supplied identity headers. Transport upgrades (, ) may change framing and latency. They must not change what a GET or POST means to the application.

Worked example: cold vs warm connect

Specify what the timing includes. In a simplified loss-free TCP plus TLS 1.3 exchange, connection setup costs roughly two round trips before a normal request; request-to-first-response adds another round trip plus server work. A warm connection avoids setup but still pays that request-response travel. Independent calls can overlap, whereas dependent calls form a serial critical path. Do not add every connection’s setup duration when estimating a parallel page load.

One API call on a new TCP + TLS 1.2 path waits for setup, then one more RTT for the response. Reuse the connection (or use HTTP/3 / 0-RTT where allowed) and setup collapses. Exact floors and the 50K QPS capacity reading are in Numbers to know.

Failure timeline: TCP HOL under HTTP/2

T0  Browser opens one HTTP/2 TCP connection; 12 streams multiplexed
T1  One packet for stream #3 is lost on a 2% lossy mobile path
T2  TCP holds the connection until retransmission; streams #1–#12 stall
T3  Page looks "hung" even though only one image stream needed the packet
T4  HTTP/3/QUIC recovers that stream independently; others keep flowing

HTTP/2 fixed HTTP-level head-of-line blocking; TCP loss can still freeze every multiplexed stream until recovery (same idea as the generations section above).

Exercises

Exercise 1

At 80ms RTT and 1% packet loss, argue for HTTP/2 vs HTTP/3 at the CDN edge. What metric would make you reverse the decision?

Exercise 2

2KB of cookies on every API call at 40K QPS. Quantify header bandwidth and propose two concrete reductions without breaking auth.

Exercise 3

A proxy strips Authorization and injects X-User-Id. What must services verify, and what must never become false?

Try it: HTTP version multiplexer on one page load

Load six small assets. Step simulated time one RTT at a time and compare sequential HTTP/1.1 turns with multiplexed HTTP/2 and QUIC-style independence for HTTP/3.

HTML

0/3 RTTs

CSS

0/3 RTTs

JS

0/3 RTTs

font

0/3 RTTs

image

0/3 RTTs

API

0/3 RTTs

Simulated time
0 ms
Assets complete
0/6
Active version
HTTP11

HTTP/1.1 typically completes one request per connection turn before starting the next. HTTP/2 multiplexes many streams on one connection.

Topic-specific references

What to remember

Default to for many web clients and service calls (including gRPC), and enable on CDN/edge for mobile. Biggest wins come from cutting unnecessary round trips: , header compression, and . In interviews, say HTTP/2 solves HTTP-level but not TCP-level blocking, and HTTP/3 addresses both.

Practice and further reading

Use these exercises to connect the lesson to a design decision. State your assumptions, trace what happens to one request, and explain the rule that must remain true even during a failure. That rule is the design's correctness invariant.

Focus for this lesson: Reason about multiplexing, head-of-line blocking, prioritization, compression, connection migration, and replay risk across HTTP versions.

Explain the decision

Why does HTTP/2 multiplexing not eliminate all head-of-line blocking?

Read the reasoning

Streams are independent at HTTP level, but TCP delivers one ordered byte stream, so a lost packet can stall data for every stream on that connection.

Apply it to a production scenario

Enabling HTTP/3 helps mobile p95 but increases CPU. How do you roll it out?

Read one approach

Advertise with Alt-Svc to a cohort, compare handshake/recovery and CPU metrics, retain HTTP/2 fallback, and isolate 0-RTT operations that are replay-safe.

Work through the numbers

At 100 ms RTT, compare a new HTTP/1.1 TLS connection needing about 2 RTT before request data with QUIC 1-RTT setup.

Read the worked calculation

The simplified setup delta is about 100 ms; include DNS, resumption, loss, and server processing separately.

Reason through a failure

What invariant applies to HTTP 0-RTT requests?

Read the recovery reasoning

Replayable early data must never perform a non-idempotent business mutation without an application replay defense.