Enable HTTP/2 on NGINX with ALPN
Serve an existing HTTPS virtual host over HTTP/2 by enabling ALPN, turning on the ngx_http_v2 module, and verifying the negotiated protocol with curl.
Task
Upgrade a production HTTPS endpoint from HTTP/1.1 to HTTP/2 without breaking older clients or a TLS-terminating load balancer.
When to use this
A public API still negotiates HTTP/1.1, so browsers open six parallel connections per host and header-heavy responses pay repeated round trips. Clients should get multiplexed streams while HTTP/1.1 remains available as a fallback.
Prerequisites
- NGINX 1.25.1 or newer with the ngx_http_v2 module compiled in
- A valid TLS certificate and private key for the hostname
- Root or sudo access to edit and reload the NGINX configuration
- curl 8.x built with HTTP/2 support
Steps
1. Confirm the running NGINX binary includes the HTTP/2 module before changing configuration.
nginx -V 2>&1 | tr " " "\n" | grep http_v2Expected output: configure arguments include --with-http_v2_module
2. Turn on HTTP/2 for the TLS listener; on NGINX 1.25.1 and later use the standalone http2 directive.
server {
listen 443 ssl;
server_name api.example.com;
http2 on;
ssl_certificate /etc/nginx/tls/api.example.com.crt;
ssl_certificate_key /etc/nginx/tls/api.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Expected output: The server block exposes HTTP/2 on port 443 while HTTP/1.1 remains available through ALPN fallback.
3. Validate the configuration and reload NGINX without dropping in-flight connections.
nginx -t && systemctl reload nginxExpected output: nginx: configuration file /etc/nginx/nginx.conf test is successful
4. Verify the negotiated protocol against the live endpoint.
curl -sI --http2 https://api.example.com/health | head -n 1Expected output: HTTP/2 200
Troubleshooting
Problem
curl still reports HTTP/1.1 after the reload
Solution
Check whether a load balancer or CDN terminates TLS in front of NGINX; HTTP/2 must be enabled at the edge that owns the TLS handshake.
Problem
nginx -t fails with unknown directive http2
Solution
The binary is older than 1.25.1; use listen 443 ssl http2; instead, or upgrade NGINX.
Limitations
- HTTP/2 improves connection efficiency but does not speed up a slow backend or database.
- Browsers require TLS for HTTP/2; h2c is only practical for service-to-service traffic.
Why move to HTTP/2
HTTP/1.1 serializes requests per connection, so browsers open several connections per origin and every request carries a full header set. HTTP/2 multiplexes many streams over one TLS connection and compresses headers with HPACK. For chatty clients the gain is fewer handshakes and less connection-level head-of-line blocking.
Configure the TLS listener
NGINX 1.25.1 moved HTTP/2 out of the listen directive. The example keeps proxy_http_version 1.1 toward the upstream because the backend still speaks HTTP/1.1; only the client-facing hop changes. ALPN negotiates h2 when the client offers it and falls back to HTTP/1.1 otherwise.
server {
listen 443 ssl;
server_name api.example.com;
http2 on;
ssl_certificate /etc/nginx/tls/api.example.com.crt;
ssl_certificate_key /etc/nginx/tls/api.example.com.key;
}
Verify the result
curl --http2 prints the protocol in the status line. A response of HTTP/1.1 200 means ALPN did not select h2: either the client lacks HTTP/2 support, an intermediary terminated TLS, or the directive was not loaded. Reload with systemctl reload nginx rather than restart so worker processes can finish in-flight requests.
Expect multiplexing to help most on pages with many small assets. Measure with a real load test before and after; do not assume a latency win for large single downloads. Because HTTP/2 is negotiated per connection, keep HTTP/1.1 enabled for legacy clients and monitoring agents.
Operational notes
Watch TLS handshake metrics and error rates after the change. If you terminate TLS at a cloud load balancer, enabling HTTP/2 only inside NGINX has no visible effect to browsers, so confirm where ALPN actually happens.