http/2在nginx中必须同时满足nginx≥1.9.5、编译含http_v2_module与http_ssl_module、openssl≥1.0.2e且正确链接、配置listen 443 ssl http2及有效ssl证书,缺一不可,否则被静默忽略。

HTTP/2 在 Nginx 中不是“开关式”功能,必须同时满足版本、编译模块、SSL 配置三重条件才能生效;缺一不可,否则 listen 443 ssl http2 会被静默忽略。
检查 Nginx 和 OpenSSL 是否真正支持 HTTP/2
很多用户加了 http2 却没效果,第一步就卡在这儿。Nginx 版本 ≥1.9.5 只是门槛,关键看它是否用兼容的 OpenSSL 编译过:
- 运行
nginx -v确认版本(如nginx version: nginx/1.20.2) - 运行
nginx -V 2>&1 | grep -o with-http_v2_module,输出必须是with-http_v2_module(不能为空或报错) - 运行
nginx -V 2>&1 | grep -i openssl,确认显示类似--with-openssl=/usr/local/ssl或 OpenSSL 版本 ≥1.0.2e;若显示1.0.1e或路径指向系统旧版(如 CentOS 7 默认的/usr/lib64/libssl.so.1.0.2k),即使 Nginx 版本够也无效 - 验证运行时链接的 OpenSSL:
ldd $(which nginx) | grep ssl,确保指向你期望的版本(比如libssl.so.1.1)
配置 listen 指令与 SSL 基础项
HTTP/2 强制要求 HTTPS,且必须显式启用 ALPN —— 这是浏览器协商协议的关键,不是配了证书就行:
-
listen行必须同时含ssl和http2,顺序无关,但缺一不可:listen 443 ssl http2;(IPv4)、listen [::]:443 ssl http2;(IPv6) -
ssl_certificate和ssl_certificate_key必须指向可读的 PEM 文件,路径错误会导致nginx -t报错 -
ssl_protocols必须包含 TLSv1.2 或更高:ssl_protocols TLSv1.2 TLSv1.3;(禁用 TLSv1.0/TLSv1.1) - 不要手动加
ssl_buffer_size或ssl_prefer_server_ciphers off等非必要项,除非明确需要调优
验证是否真跑在 h2 上,而不是假成功
很多人看到 nginx -t && systemctl reload nginx 就以为成了,但浏览器 Network 面板里协议列仍显示 http/1.1,说明 ALPN 协商失败:
- 用 Chrome 或 Edge 打开开发者工具 → Network 标签 → 刷新页面 → 查看任意请求的 “Protocol” 列,应为
h2 - 用命令行验证:
curl -I --http2 https://your-domain.com,若返回HTTP/2 200且无重定向,则基础通路正常 - 如果仍是 HTTP/1.1,重点排查:
openssl s_client -alpn h2 -connect your-domain.com:443 2>/dev/null | grep ALPN,输出应含ALPN protocol: h2;若为空,说明服务端未正确通告 ALPN - 某些 CDN(如 Cloudflare 免费版)或反向代理会终止 TLS 并降级到 HTTP/1.1 回源,此时 Nginx 自身配置再对也无用
Server Push 不是默认开启,且极易误用
http2_push 和 http2_push_preload 是可选高级特性,但依赖底层 HTTP/2 已就绪,且行为差异大:
-
http2_push是硬推送:只要匹配 location,每次响应都推,路径必须以/开头(如http2_push /style.css;),不支持查询参数(/style.css?v=1会被丢弃) -
http2_push_preload on;是响应式推送:只对响应头含Link: ; rel=preload; as=style的请求触发,需 HTML 里写<link rel="preload" href="/style.css" as="style"> - 二者不能共存于同一
location块,Nginx 会静默忽略后者 - 动态内容(PHP、proxy_pass)不触发
http2_push,仅静态文件响应有效;若要推后端生成资源,必须由上游服务发Link头 + 启用http2_push_preload
最常被跳过的点:ALPN 是协商前提,而它又依赖 OpenSSL 版本和 Nginx 编译时链接路径 —— 很多线上环境用包管理器装的 Nginx,背后仍连着系统旧 OpenSSL,nginx -V 看的是编译参数,ldd 看的才是真实依赖。











