ALPN general both
Not an HTTP header itself — a TLS extension for negotiating which application protocol (HTTP/1.1, HTTP/2, HTTP/3) runs over a connection before any HTTP is exchanged.
What it is
ALPN (Application-Layer Protocol Negotiation) isn't an HTTP header at all — it's a TLS extension that negotiates which application protocol will run over a connection during the TLS handshake, before a single byte of actual HTTP is exchanged. It's included in this reference because its protocol identifiers (http/1.1, h2, h3) show up directly in HTTP-adjacent contexts you'll encounter constantly — most visibly as the protocol-id values in the Alt-Svc header.
Before ALPN existed, negotiating HTTP/2 support required either a separate port, an Upgrade-header-based cleartext negotiation, or simply assuming support — all clunky. ALPN solved this cleanly: the client lists the protocols it supports in the TLS ClientHello, the server picks one from that list and confirms it in the ServerHello, and both sides know exactly what protocol to speak the instant the TLS handshake completes.
How it works
During the TLS handshake:
Client → Server: ClientHello
ALPN extension: ["h2", "http/1.1"]
Server → Client: ServerHello
ALPN extension: "h2"
The client offers an ordered list of protocol identifiers it's willing to speak; the server selects one it also supports and returns just that single value. If the server doesn't support ALPN at all, the connection falls back to whatever the client and server negotiate through older means (typically assuming HTTP/1.1).
Common protocol identifiers
| Identifier | Protocol |
|---|---|
http/1.1 |
HTTP/1.1 |
h2 |
HTTP/2 (over TLS) |
h2c |
HTTP/2 cleartext (rare, mostly internal/non-TLS contexts) |
h3 |
HTTP/3 (QUIC) |
These are the exact same identifiers you'll see as protocol-id values in Alt-Svc: h3=":443" — Alt-Svc is telling the client "here's an alternative you can reach via ALPN-negotiated h3."
Common mistakes and gotchas
Confusing ALPN with the Upgrade header. They solve a similar-sounding problem at different layers. Upgrade negotiates a protocol switch within an existing HTTP/1.1 connection (used for WebSocket, or historically for HTTP/1.1 → HTTP/2 cleartext upgrades). ALPN negotiates the protocol before any HTTP layer exists at all, as part of establishing TLS. Modern HTTP/2 deployments almost universally use ALPN rather than the Upgrade-based cleartext negotiation path.
Assuming ALPN works for HTTP/3. ALPN itself is a TCP-based TLS extension mechanism, but QUIC (which HTTP/3 runs over) has its own TLS 1.3 integration that also uses ALPN identifiers (h3) — just within QUIC's own handshake, not a traditional TCP+TLS handshake. This is part of why HTTP/3 can't simply upgrade an existing TCP connection the way HTTP/1.1→HTTP/2 can; it needs a wholly separate UDP-based connection, which is exactly the problem Alt-Svc exists to advertise.
Debugging protocol selection without checking ALPN. If a server unexpectedly serves HTTP/1.1 instead of HTTP/2 despite both supposedly being configured, the issue is often that the server's TLS library isn't correctly offering/accepting h2 in ALPN — worth checking with openssl s_client -alpn h2 -connect host:443 to see exactly what a server negotiates.
Real-world examples
Checking what a server negotiates via ALPN:
openssl s_client -alpn h2,http/1.1 -connect example.com:443 2>&1 | grep ALPN
# ALPN protocol: h2
A browser's typical ALPN offer:
ALPN extension: ["h2", "http/1.1"]
Most modern browsers offer h2 first, falling back to http/1.1 if the server doesn't support HTTP/2.
FAQ
Do I need to configure ALPN manually?
Almost never directly — it's handled by your TLS library/web server (Nginx, Apache, your CDN) as part of enabling HTTP/2 or HTTP/3 support. You typically just enable the protocol version in your server config, and ALPN negotiation happens automatically underneath.
Why does my server still serve HTTP/1.1 even though I enabled HTTP/2?
Check your TLS library version and configuration — some older TLS libraries or misconfigured setups don't correctly advertise h2 in ALPN even when HTTP/2 is nominally "enabled," silently falling back to HTTP/1.1. Testing directly with openssl s_client -alpn h2 is the fastest way to confirm what's actually being negotiated.
Is ALPN the same thing as the protocol identifiers in Alt-Svc?
They use the same identifier strings (h2, h3, http/1.1), and that's intentional — Alt-Svc is advertising that a given protocol is reachable, and ALPN is the actual negotiation mechanism used once a client attempts to connect using it.
Can a server support multiple protocols via ALPN simultaneously?
Yes — a server commonly supports h2 and http/1.1 on the same port, letting ALPN pick whichever the connecting client also supports, rather than requiring separate ports or configuration per protocol version.
Fun fact
Before ALPN existed, an earlier Google-created extension called NPN (Next Protocol Negotiation) served a similar purpose, but with the client making the final protocol choice rather than the server. ALPN flipped this — the server chooses from the client's offered list — specifically because giving the server final say made policy enforcement and protocol prioritization far simpler for server operators, and NPN was deprecated in favor of ALPN shortly after HTTP/2 standardization.