How do HTTP/1.1, HTTP/2 and HTTP/3 differ, and what does that mean for an iOS client?
Predict first
A page used to open six connections to one host and shard images across more hostnames. What changes with HTTP/2?
// A: nothing, more connections is always faster
// B: one connection can carry all of it, so sharding is no longer needed and can hurt
// C: the requests must be made one at a time
- A
- B
- C
Mental model. HTTP/1.1 is a single checkout lane per connection. HTTP/2 is one lane with a conveyor that mixes everyone’s items but stops entirely if one item jams. HTTP/3 gives each customer an independent conveyor.
HTTP/1.1 sends one request at a time per connection, so a page of many resources opens several connections in parallel (about six per host) and suffers head-of-line blocking: a slow response holds up the ones queued behind it. HTTP/2 multiplexes many concurrent request and response streams over a single TCP connection, compresses headers (HPACK), and allows server prioritisation, so the old tricks (domain sharding, bundling many files) become unnecessary and even harmful. Its remaining weakness is at the transport layer: a lost TCP packet stalls all streams until it is retransmitted. HTTP/3 runs over QUIC (on UDP) instead of TCP: each stream is independent so a lost packet delays only its own stream, the TLS 1.3 handshake is built into the transport (a connection is established in one round trip, and resumed with zero), and connections survive a network change (Wi-Fi to cellular) because they are identified by a connection id rather than the IP address and port.
For a client the good news is that URLSession negotiates all of this for you: it uses HTTP/2 when the server supports it (via TLS ALPN) and HTTP/3 when the server advertises it (through the Alt-Svc header or DNS HTTPS records) and the network allows UDP. There is no switch in your code. What you do control: reuse one session so connections are reused, avoid per-request sessions, do not shard hosts, keep requests small and cacheable, and prefer fewer large hosts. You can see what was used in URLSessionTaskMetrics (transactionMetrics.first?.networkProtocolName gives "http/1.1", "h2" or "h3", plus timings for DNS, connect, TLS and first byte), which is the right way to verify. Server-side, supporting HTTP/3 is an infrastructure decision; the client simply benefits. In practice remember that some corporate networks block UDP, so the client falls back to HTTP/2 transparently.
Multiplexing makes extra connections and hostnames unnecessary, and each one adds handshake cost and splits congestion control.
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Transport | TCP | TCP | QUIC over UDP |
| Concurrency | one request per connection | multiplexed streams, one connection | independent streams, one connection |
| Head-of-line blocking | per connection | at the TCP level | none across streams |
| Connection setup | TCP + TLS | TCP + TLS | combined, 1 round trip; 0 on resume |
| Network change | new connection | new connection | connection can migrate |
Common traps
- Opening a new session per request, or sharding across hostnames. It defeats multiplexing and connection reuse.
- Assuming a request used HTTP/3 because the server supports it. Check
networkProtocolNamein the task metrics; UDP may be blocked.
They might follow up with
- How do you find which protocol a request used?
URLSessionTaskMetrics,networkProtocolName. - What does QUIC give mobile clients? Independent streams and connection migration when the network changes.
Related questions
- 12.1.1 What do the URLSession configurations control, and how do default, ephemeral and background sessions differ?
- 12.2.2 How do TLS and App Transport Security protect a request, and what is certificate pinning, and why is it…
- 12.4.3 Why use NWPathMonitor instead of reachability, and how do you use connectivity information correctly?
How do TLS and App Transport Security protect a request, and what is… →