skip to content

A browser opens https://example.com. Explain the mechanism by which it ends up speaking HTTP/2 rather than HTTP/1.1, and how it would ever discover that HTTP/3 is available on that origin.

level: middleimportance: must knowfreq 45%

answer

  1. ALPN in ClientHello: h2, http/1.1 - zero extra RTT
  2. server picks; no ALPN means http/1.1
  3. h3 can't use ALPN over TCP - different transport
  4. Alt-Svc: h3=":443"; ma=86400, cached per origin
  5. DNS HTTPS/SVCB alpn=h3 for first-flight; keep h2 fallback

basics

~20 s

HTTP/2 is chosen by ALPN inside the TLS handshake: the client offers h2 and http/1.1, the server picks one, at no extra round trip. HTTP/3 cannot be negotiated that way because it needs a QUIC connection first, so the server advertises it with an Alt-Svc response header or a DNS HTTPS record.

solid answer

~60 s

**HTTP/2**: negotiated by **ALPN**, a TLS extension. In the ClientHello the browser lists the protocols it supports, typically `h2` then `http/1.1`; the server picks one and echoes it back. It costs zero extra round trips because it rides inside a handshake that was happening anyway, and it fails safe - a server that does not understand ALPN simply yields HTTP/1.1. Browsers only speak HTTP/2 over TLS, so in practice `https` plus ALPN is the whole story. **HTTP/3** cannot be negotiated the same way, because reaching a TLS handshake over TCP already committed you to the wrong transport; QUIC runs on UDP. So discovery is out-of-band: - **`Alt-Svc: h3=":443"; ma=86400`** on an HTTP/1.1 or HTTP/2 response tells the client that this origin is also reachable via h3 at that endpoint, and to remember it for `ma` seconds. The current request finishes on the current connection; a later connection attempts h3. - **A DNS `HTTPS` (SVCB) record** carrying `alpn="h3"` lets a client try HTTP/3 on the very first connection. If QUIC fails - UDP blocked, timeout - the client falls back to TCP with h2.

code

bash · 9 lines
bash
# what ALPN protocol did the server select?
openssl s_client -alpn h2,http/1.1 -connect example.com:443 </dev/null 2>/dev/null | grep ALPN

# negotiated HTTP version, plus any Alt-Svc advertisement
curl -sI --http2 https://example.com | head -1
curl -sI https://example.com | grep -i alt-svc

# force HTTP/3 (curl build with HTTP/3 support)
curl -sI --http3 https://example.com | head -1

go deeper

for a junior

Know that ALPN inside the TLS handshake selects h2, and that Alt-Svc is how a server advertises HTTP/3.

for a middle

Explain that ALPN is free and fail-safe, that the server chooses, and why HTTP/3 needs out-of-band advertisement because it changes transport.

for a senior

Add DNS HTTPS/SVCB for first-flight h3, racing and per-network fallback caching, and the operational rule that h2 stays enabled as the fallback path.

for a principal

Reason about rollout: who controls the advertisement (usually the CDN), the blast radius of a bad Alt-Svc with a long max-age, and how you would measure protocol mix and fallback rates in the field.

## Negotiating HTTP/2: ALPN Application-Layer Protocol Negotiation is a TLS extension (RFC 7301). The client puts a preference-ordered list of protocol identifiers into its ClientHello - for a browser, `h2`, `http/1.1` - and the server selects one and returns it in its half of the handshake. By the time the handshake completes, both sides know which application protocol the encrypted connection carries. Three properties matter: - **Free.** It is carried in a handshake that already had to happen for `https`. No extra round trip, no probe request, no Upgrade dance. - **Fail-safe.** A server that does not offer ALPN, or offers only `http/1.1`, gets HTTP/1.1. Nothing breaks. - **Server-decided.** The server chooses from what the client offered, so an operator can disable HTTP/2 unilaterally. ALPN replaced NPN, an earlier SPDY-era extension in which the client chose; NPN is long dead. Note that HTTP/2 over TLS also has version and cipher requirements - TLS 1.2 or better, with a blocklist of weak ciphers - and a server that negotiates `h2` with a forbidden cipher will see the connection dropped. What you will *not* see in a browser is HTTP/2 without TLS. The spec allows cleartext h2c, but no major browser implements it, so `http://` URLs are HTTP/1.1 in practice. ## Why HTTP/3 needs something else HTTP/3 runs over QUIC, which runs over UDP. To use ALPN you must already be inside a TLS handshake, and to get there over TCP you have already chosen the transport. There is no way to discover, mid-TCP-handshake, that a UDP endpoint exists. Hence advertisement. **`Alt-Svc`** (RFC 7838) is a response header naming alternative services for this origin: ``` Alt-Svc: h3=":443"; ma=86400 ``` Read as: the same origin is also served by protocol `h3` on port 443 of the same host, and this information is valid for 86400 seconds. Clients cache it per origin. HTTP/2 also has an ALTSVC frame carrying the same information out of band, and HTTP/3 responses commonly re-advertise `h3` to refresh the cache. **DNS `HTTPS`/SVCB records** (RFC 9460) close the remaining gap: the record can carry `alpn="h3,h2"`, plus hints such as IP address hints and ECH configuration, so a client learns about HTTP/3 *before* connecting and can use it on the very first request. This removes the classic 'first visit is always h2' penalty, and is how large CDNs get first-flight h3. ## Racing and fallback Because UDP is blocked or aggressively rate-limited on some corporate and mobile networks (single-digit percentages, but real), no correct client bets everything on QUIC. Typical behaviour: start the QUIC attempt and a TCP attempt in parallel, or start QUIC with a short timer, use whichever completes, and remember failure per network so the next attempt does not repeat the penalty. From the operator's side this means **you never turn off HTTP/2** when you turn on HTTP/3 - h2 over TCP remains the mandatory fallback path. ## How to check in practice Browser devtools show a Protocol column (`h2`, `h3`, `http/1.1`). On the command line, `curl -I --http2 https://...` reports the negotiated version, and `curl --http3` (in builds with HTTP/3 support) forces QUIC. `openssl s_client -alpn h2 -connect host:443` shows the ALPN result directly from the handshake. ## The mental model HTTP/2 selection is *in-band and free*; HTTP/3 selection is *advertised and racy*. That asymmetry is a direct consequence of HTTP/3 changing transport rather than only framing, and it explains why h3 adoption depends on CDNs - which set Alt-Svc and publish HTTPS records for you - far more than on your application code.

  • Why can't HTTP/3 be negotiated with ALPN the way HTTP/2 is?
    ALPN lives inside the TLS handshake, and to run a TLS handshake over TCP you have already committed to TCP as the transport. HTTP/3 needs QUIC over UDP, an entirely different connection, so by the time ALPN could speak you are on the wrong socket. ALPN identifiers such as h3 do appear inside QUIC's own TLS 1.3 handshake, but only once you have already decided to try QUIC.
  • A client has cached Alt-Svc: h3=":443" but the network blocks UDP. What should happen?
    The QUIC attempt times out or is refused and the client falls back to TCP with HTTP/2, usually racing the two so the user sees no delay. A well-behaved client also remembers the failure keyed to the current network so it does not pay the penalty on every navigation. This is why an operator must keep HTTP/2 enabled after enabling HTTP/3.
  • Does a DNS HTTPS record replace Alt-Svc?
    They complement each other. The DNS HTTPS/SVCB record is available before any connection, so it enables HTTP/3 on the first request and can carry IP hints and ECH configuration. Alt-Svc is discovered only after a first response, but it is under the origin server's direct control and can be changed instantly without waiting for DNS TTLs.

saying these in an interview costs you the question

  • Saying the client sends `Upgrade: h2` over HTTPS - the Upgrade dance is the cleartext h2c mechanism, not what browsers do
  • Claiming ALPN costs an extra round trip
  • Thinking the client picks the protocol - the server selects from the client's list
  • Believing HTTP/3 is negotiated by ALPN over the existing TCP connection
  • Assuming you can disable HTTP/2 once HTTP/3 is enabled

context