Read real HTTP headers, a curl trace, and a gRPC exchange; predict the behaviour and pick the highest-leverage fix a senior engineer would make first.
NETSenior◷ 14 min
Level
FoundationsJuniorMiddleSenior
Wire dumps and response headers are where HTTP problems are actually diagnosed. Read the exchange, predict what the browser, CDN, or QUIC stack will do, then choose the fix a senior engineer reaches for first.
Goal
Practise the loop you run in every HTTP incident: read the headers and the trace, predict the protocol-level behaviour, and reach for the structural fix before tuning anything.
The server advertises h3 via Alt-Svc but the HTTP/3 handshake fails. What is wrong, and what is the first fix?
Heads-up QUIC uses the same TLS cert as the TCP listener on 443; there is no separate QUIC port to cover. The x-quic-version: draft-29 header shows the real cause — an obsolete QUIC draft.
Heads-up max-age only controls how long the client remembers the h3 hint; it cannot cause a handshake protocol violation. The draft-29 implementation is the defect.
Heads-up HTTP/2 already worked in the first call. The explicit 'protocol violation: invalid initial packet' is a real QUIC-version mismatch, not a curl flag issue.
Snippet 2 — the cached user profile
GET /api/user/profile HTTP/2authorization: Bearer eyJ...userAHTTP/2 200 OKcontent-type: application/jsoncache-control: max-age=300{"id":"userA","email":"a@example.com"}
Quiz
Completed
Users report seeing each other's profiles intermittently. Reading only these headers, what is the bug and the correct fix?
Heads-up HTTP/2 streams never cross request bodies between connections or users. The leak is a caching-semantics bug: a per-user response marked publicly cacheable.
Heads-up Tokens belong in the Authorization header precisely because URLs leak via Referer and access logs. The bug is the missing private/Vary on the response, not the token's location.
Heads-up A short TTL does not mix users; any TTL on a publicly-cacheable per-user response leaks. The fix is scoping the cache (private/no-store), not changing max-age.
Snippet 3 — the gRPC response
HTTP/2 200 OKcontent-type: application/grpc(DATA frames: serialized protobuf message)(trailing HEADERS frame)grpc-status: 5grpc-message: user not found
Quiz
Completed
The HTTP status is 200 but grpc-status is 5 (NOT_FOUND). Why is the result in a trailer, and what breaks if a browser calls this directly?
Heads-up By design gRPC keeps HTTP status at 200 and signals the RPC outcome in grpc-status trailers; this is correct, not a bug. The trailer placement reflects end-of-stream timing.
Heads-up Trailers are about end-of-stream metadata timing, not CORS. Browsers simply don't expose HTTP trailers to JS, hence gRPC-Web.
Heads-up HTTP/2 supports trailing HEADERS frames, and gRPC relies on them. The body holds the protobuf message; the status legitimately lands in the trailer.
Both requests arrive at once over HTTP/3. Reading the RFC 9218 Priority headers, which does the server send first, and what is the rule?
Heads-up RFC 9218 inverts that intuition: lower u is more urgent. u=1 (CSS) outranks u=5 (image), so the CSS is sent first.
Heads-up That was the failure mode RFC 9218 fixed. With Priority headers honoured by modern servers and CDNs, the server reorders DATA frames by urgency.
Heads-up Multiplexing allows interleaving, but priorities tell the server how to weight DATA-frame bandwidth — favouring the lower-urgency-value (more urgent) stream.
Recap
Every HTTP incident is read in headers and traces: an Alt-Svc h3 hint backed by a draft-QUIC stack wastes an RTT per client; a per-user response missing private/no-store and Vary leaks across a shared cache; gRPC’s real outcome lives in a trailing HEADERS frame (200 at the HTTP level) and needs gRPC-Web for browsers; and RFC 9218 urgency is inverted — lower u is more urgent. Read the wire, predict the protocol behaviour, then apply the structural fix.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.