26. HTTP/1.1 vs HTTP/2 vs HTTP/3 in the browser
The protocol version genuinely changes how you should write browser code:
| Connections per origin | Multiplexing | Practical JS implication | |
|---|---|---|---|
| HTTP/1.1 | ~6 (browser-enforced cap) | None — one request per connection at a time | Firing >6 concurrent fetch() calls to the same origin queues the rest; domain sharding (splitting assets across subdomains) was a real HTTP/1.1-era workaround |
| HTTP/2 | 1 (all requests multiplexed over it) | Yes, many parallel streams per connection | Domain sharding actively hurts HTTP/2 (extra handshakes for no benefit) — bundle to fewer origins, not more |
| HTTP/3 (QUIC/UDP) | 1, but streams don't head-of-line-block each other at the transport level | Yes, independent streams | Best on lossy mobile networks — one dropped packet no longer stalls unrelated in-flight requests, unlike HTTP/2 over TCP |
Resource hints in your HTML directly influence connection setup timing:
<link rel="preconnect" href="https://api.example.com">
<!-- Opens the TCP+TLS connection to api.example.com immediately, before any
fetch() call needs it — saves a full round trip on the first real request. -->
<link rel="dns-prefetch" href="https://cdn.example.com">
<!-- Cheaper than preconnect: resolves DNS only, for origins you'll need
later but not immediately. -->
<link rel="preload" href="/fonts/main.woff2" as="font" crossorigin>
<!-- Tells the browser "fetch this now, I know I'll need it soon" — for
resources the parser wouldn't otherwise discover until late (fonts
referenced only inside CSS, for instance). -->