Reference guide · dns-ssl · Published 2026-08-16 · 3 min read
ALPN and HTTP/2 negotiation
How ALPN negotiates HTTP/2 inside the TLS handshake, why a mismatch silently falls back to HTTP/1.1, and where to check it.
- ·How ALPN fits TLS
- ·Why HTTP/2 needs it
- ·Diagnose the fallback
The one-line picture
HTTP/2 over TLS is chosen during the TLS handshake, not afterwards. In the ClientHello, the browser offers a list of protocol identifiers (ALPN labels, for example h2, http/1.1). The server answers with the one it supports and prefers. If the server never offers h2 or the offer is configured out, the browser falls back to http/1.1 silently. ALPN is the extension that decides the version.
How ALPN fits into the TLS handshake
TLS versions negotiate the keys and the encryption. ALPN is an extension inside that handshake that negotiates the application protocol over the still-encrypted connection. Because both sides need to pick the protocol inside TLS (the encrypted channel means neither can look at the payload), ALPN must be part of the handshake rather than a later HTTP header.
The flow:
- Browser sends
ClientHellocontainingALPN: h2, http/1.1. - Server picks the highest it supports, here
h2. - Server sends
ServerHelloechoingh2. - Communication proceeds over HTTP/2.
If the server's ALPN list contains only http/1.1, or the server does not implement ALPN at all, the browser sends the request over HTTP/1.1. The site still loads; the HTTP/2 features simply never turn on.
Why HTTP/2 is stuck to ALPN (and why http/1.1 still works)
HTTP/2 was designed to be negotiated with ALPN on secure connections. Browsers only try HTTP/2 when the handshake offered it. A web server that turned on HTTP/2 in its config but sits behind or on a TLS layer that strips ALPN, a load balancer that proxies TLS without protocol switching, or an old OpenSSL build, all produce the same symptom: curl --http2 shows HTTP/1.1 and no error.
Find the negotiated protocol
curl -I --http2 https://example.com/
# HTTP/2 200
openssl s_client -connect example.com:443 -alpn h2 -servername example.com 2>/dev/null | grep -i 'ALPN'
# ALPN protocol: h2
HTTP/2 200andALPN protocol: h2mean the negotiation picked HTTP/2.HTTP/1.1 200with the TLS lookup showing the ALPN list empty, or the server only advertisinghttp/1.1, means the fallback happened.
The typical fixes by layer
- nginx: make sure the listen entry enables HTTP/2 (
http2on the listen directive, or the newerhttp2 on;/ server-wide setting), and that the TLS module carries ALPN. The most common miss is a reverse proxy or WAF in front that terminates TLS and simply does not forward the ALPN extension. - Apache:
Protocols h2 http/1.1or enablingmod_http2(LoadModule http2_moduleand the H2 configuration). The TLS endpoint must advertise ALPN. - Cloudflare/edge proxies: the edge terminates TLS and performs ALPN. If the origin is direct, some hosts disable
h2. Check the origin type, not only the edge. - A WAF or reverse proxy in front: any device that strips ALPN from the handshake silently drops HTTP/2 for everything behind it.
Where HTTP/2 shows up in your metrics
HTTP/2 multiplexes streams; a waterfall full of separate parallel connections, or a timeout where the same resource repeats, can be a symptom you are on HTTP/1.1 without intending to be. If the connection fails on the certificate layer before ALPN is even read, that problem belongs to the TLS handshake article.