如果你想让 Nginx 真正启用并稳定运行 HTTP/2,仅仅在配置中加入 http2 远远不够,还必须同时满足四个关键前提:第一,Nginx 版本必须高于 1.9.5;第二,编译时需要确认已经包含 http_v2 模块;第三,底层依赖的 OpenSSL 版本必须 ≥1.0.2,且动态链接必须正确;第四,listen 指令必须配置为 443 ssl http2,并同时启用 TLSv1.2 与 ALPN 协议支持。只要其中任意一个环节出现问题,浏览器通常都会静默降级回 HTTP/1.1,而不会给出明显报错提示。

HTTP/2 在 Nginx 中并不是“配置后立刻生效”,而是必须同时满足版本、模块、OpenSSL 链接和 TLS 配置这四项条件;任何一项缺失,浏览器都会自动 fallback 到 HTTP/1.1,并且通常不会报错。
检查 Nginx 和 OpenSSL 是否真正支持 HTTP/2
很多人在配置了 listen 443 ssl http2 之后,依然看不到连接协议显示为 h2,问题往往不是 Nginx 配置写错,而是底层环境本身并未完整支持 HTTP/2。
- 执行
nginx -v,确认输出的版本号 ≥nginx/1.9.5(建议使用1.20.2+或当前最新稳定版本) - 执行
nginx -V 2>&1 | grep -o with-http_v2_module,返回结果必须是with-http_v2_module(不能为空,也不能报错) - 执行
nginx -V 2>&1 | grep -i openssl,确认路径指向你预期使用的 OpenSSL(例如--with-openssl=/usr/local/ssl),并且版本 ≥1.0.2e;如果显示为1.0.1e,或仍然指向系统旧版库(例如 CentOS 7 默认的/usr/lib64/libssl.so.1.0.2k),即使 Nginx 版本达标,HTTP/2 也无法正常工作 - 执行
ldd $(which nginx) | grep ssl,确保 Nginx 实际链接的是你编译时指定的 OpenSSL 动态库(例如libssl.so.1.1),而不是系统默认的旧版本 SSL 库
配置 listen 指令与必要的 SSL 参数
在 Nginx 中启用 HTTP/2 必须基于 TLS,同时还依赖 ALPN 协商机制——这并不是“开启 HTTPS 后自动拥有”的能力,而是需要明确配置和确认支持。
listen指令必须同时包含ssl和http2:如果只写listen 443 http2,或者只写listen 443 ssl,都无法正确启用 HTTP/2,且可能被 Nginx 静默忽略- 正确写法只有两种:
listen 443 ssl http2(IPv4)以及listen [::]:443 ssl http2(IPv6);虽然ssl和http2的先后顺序不影响结果,但两者必须同时存在 ssl_certificate和ssl_certificate_key必须指向可读取的 PEM 格式证书文件(不能使用 DER 或 PFX 格式)ssl_protocols必须启用 TLSv1.2 或更高版本,例如:ssl_protocols TLSv1.2 TLSv1.3;建议禁用TLSv1.0和TLSv1.1,否则可能导致 ALPN 协商被跳过
验证是否真的使用 h2,而不是“看似成功”
nginx -t && systemctl reload nginx 执行成功,并不代表 HTTP/2 已经真正生效。只要 ALPN 协商失败,Nginx 就会自动退回到 HTTP/1.1,而且通常不会有任何明显提示。
- 在浏览器中打开 DevTools → Network → 刷新页面 → 右键表头勾选
Protocol,任意请求都应显示为h2,而不是http/1.1 - 使用命令行验证:
curl -I --http2 https://your-domain.com,如果返回结果是HTTP/2 200,并且没有发生重定向,才说明基础链路已正常支持 HTTP/2 - 如果结果仍然是 HTTP/1.1,可进一步执行
openssl s_client -alpn h2 -connect your-domain.com:443 2>/dev/null | grep ALPN,检查服务端是否通告ALPN protocol: h2;如果没有任何输出,通常说明后端并未启用 ALPN,常见原因包括 CDN 中间层拦截,或 OpenSSL 动态链接错误
最常被忽略的关键点,其实是 OpenSSL 在运行时的实际链接版本,以及 ALPN 是否真的对外通告。这两个底层条件一旦不匹配,前面的 Nginx 配置即使全部正确,也依然无法让 HTTP/2 生效。与其反复检查 nginx.conf,不如先通过 ldd 和 openssl s_client 把底层依赖链路彻底确认清楚。
