← Recipes

阅读中文版本

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.

~25 minnginxhttpperformance

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_v2

Expected 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 nginx

Expected 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 1

Expected 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.