What you'll learn: By the end of this lesson you will be able to explain what HTTP/1.1, HTTP/2 and HTTP/3 share and what they change, why HTTP/2 multiplexes but still suffers from TCP head-of-line blocking, how HTTP/3 over QUIC avoids part of that, how a browser discovers and falls back from HTTP/3, how to see which version a request used, and which front-end habits (domain sharding, server push) to drop or treat with care.
Imagine a post office with one clerk per queue. HTTP/1.1 is a counter where each customer must finish before the next is served, so you open several counters to go faster. HTTP/2 is one counter that takes many parcels at once but loads them onto a single truck: if the truck stalls, every parcel waits. HTTP/3 gives each parcel stream its own lane on the road, so a stall in one lane does not stop the rest.
The running example is the product page from the previous lesson, now with about 60 resources (illustrative) spread over two origins: scripts, styles, product images and fonts. The question is how those requests travel once the connection exists. The set-up cost (DNS, TCP, TLS) is in the request lifecycle lesson; caching is in HTTP Caching for the Front End.
The core idea
- The meaning of HTTP does not change between versions; the way messages are carried does. Methods, status codes and fields are defined once (RFC 9110). RFC 9112, 9113 and 9114 map them onto the wire for HTTP/1.1, HTTP/2 and HTTP/3. MDN states that HTTP/3 "has the same semantics as earlier versions".
- The versions differ in how many requests can be in flight on one connection, and in what one lost packet costs. HTTP/1.1 carries one at a time. HTTP/2 interleaves many on one TCP connection. HTTP/3 interleaves many on one QUIC connection.
- You influence the version by what your servers and CDN support and advertise. The browser picks. Nothing in your JavaScript chooses it, but your design (how many origins, how many hints, how you prioritise) behaves differently under each.
Figure 1. One set of semantics, three ways to carry it
How to read this: the top box is what your code sees. The three boxes below are ways to put it on the network. Only HTTP/3 leaves TCP.
HTTP/1.1: one request at a time
RFC 9113's introduction describes the starting point: HTTP/1.0 allowed one outstanding request per TCP connection, and HTTP/1.1 "added request pipelining, but this only partially addressed request concurrency and still suffers from application-layer head-of-line blocking". So clients "use multiple connections to a server to make concurrent requests". MDN confirms browsers commonly open up to six TCP connections per origin and that pipelining is not active by default in modern browsers, because buggy proxies make it unreliable.
That shaped a generation of front-end habits: concatenate files, merge images into sprites, and spread assets across several host names ("domain sharding") to get more connections. Each extra host costs a DNS lookup, a connection and a handshake, which is why the previous lesson counts origins.
HTTP/2: many streams, one connection
HTTP/2 (RFC 9113) keeps the semantics and changes the packaging. MDN lists three differences: it is binary rather than text, multiplexed ("parallel requests can be made over the same connection"), and compresses headers. The RFC explains the unit: each request and response exchange gets its own stream, and messages are split into frames that can be interleaved on one connection. "Streams are largely independent of each other, so a blocked or stalled request or response does not prevent progress on other streams."
Four details a front-end designer should know:
- One connection per origin. Clients "SHOULD NOT open more than one HTTP/2 connection to a given host and port pair". Six connections become one.
- Header compression is stateful. HPACK keeps a table per connection (4,096 bytes initially), so repeated fields such as cookies and user agents cost little after the first request.
- Concurrency has a cap. The server announces
SETTINGS_MAX_CONCURRENT_STREAMS. The RFC recommends no smaller than 100 and notes that, initially, there is no limit. Flow control windows start at 65,535 octets per stream and per connection; servers tune these, not pages. - It rides on TCP. The ALPN identifier is
h2, negotiated inside the TLS handshake. RFC 9113 requires TLS 1.2 or higher for HTTP/2 over TLS and deprecates the oldh2cupgrade.
The part HTTP/2 does not fix
The RFC is blunt: "TCP head-of-line blocking is not addressed by this protocol." All streams share one ordered byte stream. If one TCP segment is lost, the operating system holds the data behind it until the retransmission arrives, and every stream waits, including streams whose own data was not lost. On a clean network this is invisible. On a lossy network the stall hits every stream at once, which is the motivation for HTTP/3.
Prioritisation and push
With many requests in flight, the order matters. RFC 9218 defines an HTTP-version-independent Priority header: u=0 to u=7 urgency (default 3, lower is more urgent) and an i flag for incremental delivery. MDN notes the server is not bound by it. web.dev says priorities matter most when resources compete for bandwidth and that "CDNs don't implement HTTP/2 prioritization uniformly, and similarly for HTTP/3", so fetchpriority="high" on the key image is a hint to verify.
Server push let a server send resources before the client asked. Chrome's team wrote that it was "problematic" and that benefits were "often difficult to realize", reporting about 1.25% of HTTP/2 sites using it, and disabled it by default in Chrome 106. Its stated alternative is 103 Early Hints, where the server sends Link preload or preconnect hints before the final response. MDN notes Early Hints is recommended only over HTTP/2 or later. Do not design around push.
HTTP/3: HTTP over QUIC
HTTP/3 (RFC 9114) uses QUIC (RFC 9000), a "UDP-Based Multiplexed and Secure Transport". QUIC provides streams, flow control and loss recovery itself, and integrates TLS 1.3 (RFC 9001, which forbids older TLS versions). HTTP/3 keeps HTTP/2's idea of frames on streams but moves the multiplexing down into the transport.
- Per-stream loss recovery. RFC 9000 describes "avoidance of head-of-line blocking across multiple streams": when a packet is lost, "only streams with data in that packet are blocked", though a packet that carries several streams blocks all of them. Because HPACK needs in-order delivery, which QUIC does not give across streams, HTTP/3 uses QPACK instead.
- One combined handshake. RFC 9001 says most new connections can be "established and secured within a single round trip" absent loss, and that clients can often send data immediately on a later connection (0-RTT). The 0-RTT replay warning from the previous lesson applies: RFC 9114 requires the HTTP anti-replay mitigations for 0-RTT.
- Connections that survive a network change. Connection IDs let a QUIC connection migrate to a new path, for example when a phone moves from Wi-Fi to cellular (only clients can migrate in this version).
- Fallback. The ALPN identifier is
h3. RFC 9114 says connectivity problems such as blocked UDP "can result in a failure to establish a QUIC connection; clients SHOULD attempt to use TCP-based versions of HTTP in this case."
MDN says HTTP/3 is supported by most major browsers, including Chromium-based browsers and Firefox (page last modified 20 March 2026, read on 8 November 2026). The caniuse data (updated 5 November 2026) agrees: it reports about 95% of global usage with support, with Safari enabled for all users of version 16 and newer since September 2024 and Samsung Internet not supported. Support changes, so check current data before you rely on it.
Figure 2. One lost packet on HTTP/2 over TCP and on HTTP/3 over QUIC
How to read this: the same single loss. Under TCP the gap blocks everything behind it. Under QUIC only stream A waits, unless a packet carried data for several streams.
How a browser ends up on HTTP/3
A browser that knows nothing about your server connects the usual way and negotiates h2 or http/1.1 by ALPN. HTTP/3 is learned, not assumed, in one of two ways.
Alt-Svcheader. An origin can advertise an equivalent HTTP/3 endpoint, for exampleAlt-Svc: h3=":443"; ma=86400. RFC 9114 says a client "MAY attempt to establish a QUIC connection" to it. MDN:mais the number of seconds the entry is fresh, defaulting to 24 hours if omitted, andAlt-Svc: clearinvalidates all alternatives. Using one "does not introduce additional round trips".HTTPSDNS record. RFC 9460 lets a domain listalpnvalues in DNS, so a client can learn abouth3before its first connection. A client that supports both TCP and QUIC may try both.
Figure 3. Discovering HTTP/3 with Alt-Svc, with TCP fallback
How to read this: the first visit is usually not HTTP/3. A cached advertisement (or an HTTPS DNS record) is what makes later connections try QUIC, and the TCP fallback is what makes that safe.
Seeing the version a request used
Each Resource Timing entry has nextHopProtocol, "the network protocol used to fetch the resource, as identified by the ALPN Protocol ID": http/1.1, h2 or h3. It reads as an empty string for cross-origin resources whose origin does not send Timing-Allow-Origin; the Resource Timing and Fetch standards also default the protocol to empty when no connection was recorded, so expect empty values for some other entries too.
const counts = {};
for (const e of performance.getEntriesByType("resource")) {
const p = e.nextHopProtocol || "unknown";
counts[p] = (counts[p] || 0) + 1;
}
console.log(counts); // for example { h2: 41, h3: 12, unknown: 7 } (illustrative)
Group field data by this value to see which share of users reached HTTP/3. Remember ma: the first request of a user's first visit will not show h3.
What changes for your front-end design
Treat these as judgement from the sources above, and confirm each by measurement.
- Drop domain sharding. MDN calls it a deprecated technique that is "even detrimental to performance" under HTTP/2. Extra host names mean extra set-ups, and the single connection already handles parallel requests.
- Know connection coalescing. HTTP/2 allows a connection to be reused for several URI authorities when the origin server is authoritative, for
httpswhen "a certificate that is valid for the host in the URI" was presented (RFC 9113, 9.1.1; RFC 9114, 3.3, has a similar rule for HTTP/3). A server that does not want reuse can answer421 (Misdirected Request). Hosts that share a certificate and address may share a connection even when you thought they were separate. - Re-measure bundling. Many small files no longer queue behind each other the way they do on HTTP/1.1, but each is still a request that needs scheduling, and prioritisation support varies. Decide per page from measurements, not from a rule.
- Prioritise explicitly. Use
fetchpriority="high"on the LCP image and keep unneeded requests off the critical path. - Replace push with Early Hints for preload and preconnect hints.
- Keep set-up thinking. One connection per origin means origins are still the unit of set-up cost.
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Transport | TCP | TCP | QUIC over UDP |
| Concurrency on one connection | One request at a time | Many streams | Many streams |
| Connections per origin in a browser | Several | One (a SHOULD) | One (a SHOULD, per IP and port) |
| Effect of one lost packet | Stalls that connection | Stalls all streams | Stalls streams with data in that packet |
| Header compression | None | HPACK | QPACK |
| Discovery | Default | ALPN h2 |
Alt-Svc or HTTPS record, ALPN h3 |
Three illustrations
A minimal example (the arithmetic). Suppose, as an illustration, 60 resources, a 50 ms round trip and negligible bandwidth and server time. On six HTTP/1.1 connections each connection serves ten requests one after another: ten round trips, 500 ms of waiting. On one HTTP/2 connection, with a stream limit of at least 100, all 60 requests can be in flight together: about one round trip, 50 ms, before bandwidth takes over. Six handshakes also become one. Real pages discover resources in stages and share bandwidth, so the gain is smaller. The point is the shape of the difference.
A realistic example (the product page). The page's HTML names a stylesheet, a script bundle and 40 images on the asset origin. The CDN serves HTTP/2 to the browser and advertises h3. On the first visit the browser uses HTTP/2. On later visits it may use HTTP/3. The design stays the same: preconnect to the asset origin, fetchpriority="high" on the hero image, no sharded hosts.
A failure or edge case (HTTP/3 advertised but blocked). A network drops outbound UDP. The browser has a cached Alt-Svc entry, tries QUIC and gets no answer. Per RFC 9114 it should fall back to TCP, so the page still loads over HTTP/2. Any delay depends on how the browser orders its attempts, which the sources do not specify, so measure it.
The other side of this topic. A load balancer or CDN may speak one HTTP version to browsers and another to your origin servers: the semantics are shared, and each hop negotiates its own connection.
In practice: moving a storefront off sharded hosts
This is a realistic scenario, not a case history. It is a composite built from common production patterns. It describes no specific company or product, and every number in it is an illustrative assumption.
The situation
A storefront serves product images from four sharded host names, img1 to img4, set up years ago for HTTP/1.1. The CDN now supports HTTP/2 and HTTP/3. Field data shows slow image-heavy pages, with many connection set-ups per page view. Four engineers own the front end.
Starting architecture
Figure 4. Four sharded image hosts, four set-ups
How to read this: each shard is its own origin with its own DNS lookup, connection and handshake, and under HTTP/2 or HTTP/3 the parallelism they were created for is already provided by one connection.
Options considered
| Option | What it fixes | What it costs | Risk |
|---|---|---|---|
| Keep the shards | Nothing new | Four set-ups per cold page | Detrimental under HTTP/2 per MDN |
| Merge to one image host | Three set-ups, one connection to schedule | URL migration, cache warm-up | Old URLs in caches and emails |
Merge and add fetchpriority and preconnect |
Also orders the work | More markup to maintain | CDN prioritisation varies |
The decision
| Field | Decision record |
|---|---|
| Context | Shards were created for HTTP/1.1's per-connection limit; the CDN serves HTTP/2 and advertises HTTP/3. |
| Decision | Move to one image host, keep old shard names resolving (redirect or alias) during a transition, preconnect to the image host, mark the hero image fetchpriority="high". |
| Consequences | Fewer set-ups and one connection to schedule; migration work and a period with two URL shapes. |
| Revisit when | Field data shows HTTP/3 share and image timing by protocol. |
Rollout and verification
- Baseline connection set-up counts and image load times by
nextHopProtocolin field data. - Roll out to a small share of traffic behind a flag, and compare.
- Keep the old shard names working until traffic to them is gone, and watch for broken image URLs.
The 2 a.m. failure
During an unrelated network incident an engineer blocks UDP 443 at the edge. The Alt-Svc header, sent with a long ma, keeps telling returning browsers that h3 is available. The incident ends, but image times may creep up for users whose browsers still attempt QUIC first.
First three checks: (1) group field timings by nextHopProtocol and look for a falling h3 share with rising connect times; (2) check the edge's UDP rules and the Alt-Svc header the server is sending; (3) test from a client network to confirm QUIC connections fail. Mitigation: restore UDP 443, or send Alt-Svc: clear and lower ma until it is fixed. Follow-up: treat the Alt-Svc header and the UDP rule as one setting in change reviews, and alert when the h3 share changes sharply.
Architect's notes
- Do not tune for one version. Users reach you over all three. Pick designs that are not harmful on any.
- Advertise HTTP/3 only where UDP is allowed and monitored, and keep
mashort while you learn. - Count origins, not just requests. One connection per origin keeps set-up the dominant cost.
- Verify prioritisation in the field. CDNs differ.
Common mistakes
- Keeping domain sharding after moving to HTTP/2. It adds set-ups and removes nothing.
- Relying on server push. Browsers have removed it; use Early Hints.
- Assuming HTTP/2 fixes loss. TCP head-of-line blocking remains.
- Testing only the first visit for HTTP/3. It is usually discovered after the first response.
- Forgetting that 0-RTT requests can be replayed. Keep state-changing endpoints idempotent.
Key takeaways
- HTTP semantics are shared across versions; HTTP/1.1, HTTP/2 and HTTP/3 differ in framing, transport and concurrency.
- HTTP/1.1 carries one request at a time per connection, so browsers open several.
- HTTP/2 multiplexes streams on one TCP connection and compresses headers, but TCP head-of-line blocking remains.
- HTTP/3 runs on QUIC over UDP with independent streams, QPACK and a handshake combined with TLS 1.3.
- Browsers learn about HTTP/3 through
Alt-Svcor theHTTPSDNS record and fall back to TCP if QUIC fails. nextHopProtocolshows the version used; drop sharding and push, and measure bundling and priorities.
Practice exercises
Exercise 1 - Starter (30 min): Run the nextHopProtocol snippet on a few sites you use and note which origins use which version.
Exercise 2 - Intermediate (2 hours): For a page you work on, count the origins and the requests per origin, estimate the HTTP/1.1 round trips for the busiest origin (requests divided by six) against HTTP/2, and compare with a network recording.
Exercise 3 - Advanced (half day): Write a rollout and rollback plan for enabling HTTP/3 at your CDN, including the Alt-Svc header, the UDP rule, the field metric you will watch and the condition for Alt-Svc: clear.
Related reading
In this course:
- DNS, TCP, TLS and the Request Lifecycle - the set-up this lesson assumes.
- HTTP Caching for the Front End - when no request is needed at all.
- How a Browser Actually Works - where the network thread sits.
Elsewhere on the site:
- System Design: The Request Lifecycle - the same journey from the back end.
Official documentation
- RFC 9113: HTTP/2 - streams, frames, connection reuse and push.
- RFC 9114: HTTP/3 - HTTP over QUIC, discovery and fallback.
- RFC 9000: QUIC - streams, loss and migration.
- RFC 9001: Using TLS to Secure QUIC - the combined handshake and 0-RTT.
- MDN: Evolution of HTTP - the versions in context.
- web.dev: Fetch Priority - prioritising resources and CDN caveats.