HTTP design: priorities, WebTransport, and semantic correctness
RFC 9218 extensible priorities replace HTTP/2''''s broken dependency trees; MASQUE and WebTransport extend QUIC beyond request-response; semantic correctness — right status codes, right Vary, right CORS — is where CDN and caching bugs hide.
HTTP multiplexes streams. But without priorities, the server streams them in arrival order — which may put a low-priority image before the blocking CSS that pauses rendering. RFC 9218 replaces HTTP/2’s abandoned priority tree with a two-field urgency signal. Meanwhile, WebTransport turns the QUIC connection into a general-purpose bidirectional tunnel — and semantic mistakes in status codes and Vary headers quietly break CDN caching for millions of users.
RFC 9218: Extensible Priorities
HTTP/2’s original priority scheme (dependency trees with weights, RFC 7540 § 5) was complex and poorly implemented. Browsers and servers disagreed on interpretation — Cloudflare reported that in some deployments, priorities made performance worse than no priorities at all, because mismatched implementations caused servers to delay high-priority streams while serving junk.
RFC 9218 (Extensible Priorities for HTTP) deprecates the tree-based scheme and defines a simpler two-field signal:
Priority: u=0, iu(urgency, 0–7): lower number = more urgent.u=0for render-blocking HTML,u=1for critical CSS,u=3for stylesheets,u=5for async scripts and fonts,u=7for background prefetch.i(incremental flag): when set, the server streams this response incrementally — sending a chunk, then switching to higher-urgency streams, then returning. Used for large bodies where partial data is useful.
The client sends a Priority: header (HTTP/2 and HTTP/3) or a PRIORITY_UPDATE frame (HTTP/3). The server honours these to schedule DATA frame transmission order across streams.
Browser support as of 2026: Chrome, Edge, Safari, and Firefox all ship Priority header support. Cloudflare, Fastly, and major CDNs have enabled it. Reported page-load improvements: up to 37% in some Cloudflare scenarios (by delivering render-blocking resources before images).
▸Why this works
Why the old priority tree failed. RFC 7540’s dependency tree let clients declare that stream #5 depends on stream #3 with a weight of 32. Servers were supposed to allocate bandwidth proportionally. In practice: browser implementations computed trees differently across versions; server implementations didn’t agree on weight semantics; and CDN edge nodes — which typically forward without re-interpreting priority — ignored it entirely. The result was a complex protocol feature that provided no consistent benefit. RFC 9218’s two-field model (urgency + incremental) is simple enough that every implementation agrees on its semantics.
MASQUE and QUIC Datagrams
MASQUE (Multiplexed Application Substrate over QUIC Encryption) is a family of IETF protocols that use HTTP/3 as a tunnelling layer:
- CONNECT-UDP (RFC 9298): the client sends a CONNECT-UDP request to an HTTP/3 proxy; the request body becomes a tunnel for encapsulated UDP traffic.
- CONNECT-IP: tunnels IP packets, enabling a full IP-over-QUIC VPN.
Apple’s iCloud Private Relay uses MASQUE: traffic flows through Apple Relay 1 (sees IP, not content) → Apple Relay 2 (sees content, not IP) → origin. This splits trust between two providers: neither alone can correlate IP and content.
QUIC DATAGRAM frames (RFC 9221) carry unreliable encrypted payloads inside a QUIC connection — sent once, not retransmitted if lost. This enables:
- Real-time media with strict latency budgets (voice, video — where retransmission arrives too late to be useful).
- Game state updates where a missed position update is harmless and a delayed one is wrong.
WebTransport (W3C + RFC 9298-related) exposes QUIC’s full capability to browser JavaScript: both reliable streams (like WebSocket) and unreliable datagrams (like UDP) from a browser page. Unlike WebSocket, WebTransport supports multiple concurrent streams per connection and does not require the upgrade dance. Chromium ships WebTransport; Firefox 124+ as well; Safari is behind a flag as of 2026.
- WebTransport — reliable streams
- Yes (multiple, multiplexed)
- WebTransport — unreliable datagrams
- Yes (QUIC DATAGRAM)
- WebSocket — reliable
- Yes (one bidirectional channel)
- WebSocket — unreliable datagrams
- No
- SSE — reliable
- Server-to-client only
- WebTransport browser support (2026)
- Chrome, Edge, Firefox 124+; Safari behind flag
Authentication schemes
Modern web apps use Authorization: Bearer <token> carrying a JWT or opaque session token. The HTTP layer transports the credential; OAuth 2.0 / OIDC defines how tokens are obtained.
- Bearer tokens in headers — correct. Tokens in HTTP headers do not leak via
Referer, browser history, or server access logs (which log the URL but typically redact headers). - Tokens in URLs — dangerous.
?token=abcappears inRefererheaders sent to third-party resources, in browser history, in CDN and reverse-proxy access logs, and in analytics systems. Never put sensitive credentials in query strings. - Basic auth (
Authorization: Basic base64(user:pass)) — survives for internal API tooling. Digest auth is effectively retired. - WWW-Authenticate — a 401 response includes this header to tell the client which scheme to use and with what parameters (realm for Basic, issuer for Bearer).
HTTP semantic correctness pitfalls
The HTTP machinery — browser retry logic, CDN caching, proxy rewriting — depends on correct status codes and headers. Common mistakes that cause production bugs:
Wrong status codes:
200 OKfor an error body{"error": "not found"}— CDNs cache the 200, proxy retry logic doesn’t trigger, browser console shows no error.200 OKwith{ "success": false }— breaks circuit breakers and health checks.301 Moved Permanentlywhen the redirect is temporary — browsers cache 301 forever; changing it back requires clearing browser caches.
All three mistakes share the same root: the HTTP machinery acts on the status code, not the response body. When you return the wrong code, you lose every layer of automatic behaviour that HTTP infrastructure provides.
Vary header mistakes:
- No
Vary: Accept-Encodingon compressed responses — a cache may serve a Brotli-compressed body to a client that declaredAccept-Encoding: gziponly, causing garbled content. - No
Vary: Authorizationon auth-differentiated cacheable responses — the CDN serves one user’s private data to another.Cache-Control: privateprevents this at CDN level butVary: Authorizationis the correct signal. - Overly broad
Vary: Cookie— causes a cache miss on every request since cookies differ per user; defeats caching entirely for authenticated users.
These three Vary mistakes share a pattern: the cache stores or serves the wrong variant. When you add or change a Vary header, always ask: which request header actually changes the response body, and which cache layer will see both variants?
Cache-Control: private vs Vary: Authorization: use Cache-Control: private for user-specific data that must not be shared via CDN (and add no-store for sensitive data). Vary: Authorization tells shared caches to store separate variants per Authorization header value — correct for API responses that differ by token but are otherwise cacheable (e.g. by-user configuration data).
Auth endpoints returning 403 instead of 401: 401 Unauthorized means “authenticate first”; 403 Forbidden means “authenticated but not permitted”. Browsers and API clients handle these differently — 401 triggers credential prompts or refresh-token flows; 403 should not.
A CDN is serving stale user-specific API data to the wrong users. Debug the caching configuration.
A real-time multiplayer game backend serves ~100k concurrent connections, mostly mobile clients on cellular. Pick the protocol stack.
Which RFC deprecates HTTP/2's tree-based priority scheme and defines the simpler urgency-based Priority header?
Design the protocol layer for a global SaaS API serving 10k req/s from 50+ countries — mix of REST + WebSocket-style real-time, with mobile priority.
- Average user RTT to your edge: 30–200 ms.
- Lossy networks expected (mobile, satellite, hotel Wi-Fi).
- Backwards compatibility with curl, fetch(), and gRPC-Web clients.
- Auth via bearer tokens; sensitive endpoints must not be cached anywhere.
- Serve all three HTTP versions; ALPN and Alt-Svc handle selection.
- WebTransport for real-time on h3 with WebSocket-h2 fallback.
- Idempotency-Key on POST for safe retries across lossy networks.
- Cache-Control: no-store for sensitive endpoints; max-age + ETag for public.
- Monitor h3 fallback rate by ASN as a network-health signal.
- Bearer tokens in Authorization header only — never in query strings.
- 01Why did HTTP/2's priority dependency tree fail and what did RFC 9218 replace it with?
- 02What is the difference between WebTransport and WebSocket?
- 03List three semantic HTTP mistakes that cause CDN caching bugs in production.
RFC 9218 replaced HTTP/2’s complex priority dependency trees with a two-field signal: urgency (u=0–7, lower = more important) and incremental (i, stream partial data between higher-priority work). All major browsers and CDNs support it as of 2026. MASQUE (CONNECT-UDP, CONNECT-IP) tunnels arbitrary traffic through HTTP/3 — used in iCloud Private Relay. QUIC DATAGRAM frames (RFC 9221) carry unreliable encrypted payloads inside a QUIC connection; WebTransport exposes both reliable streams and unreliable datagrams to browser JavaScript. Authentication tokens belong in the Authorization header — never in URLs, which leak via Referer and access logs. Semantic correctness in status codes, Vary headers, and Cache-Control scope determines whether CDN and proxy caching behaves correctly; getting it wrong causes security leaks and stale-data bugs at scale. Now when you review a new API response in production, you’ll scan the status code, Cache-Control scope, and Vary list first — because that’s where the silent CDN bugs hide.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
appears again in5
- MVCC: why readers and writers never wait for each otherjunior
- Act 7 in depth: sharding, co-location, and the seven-tier tradeoff cascademiddle
- Observability, anti-patterns, and production triagesenior
- Raft in the real world: partitions, slow disks, and client routingmiddle
- Raft in production: membership changes, Multi-Raft, and observabilitysenior
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.