HTTP/2推送配置错误会导致浏览器重复加载资源,因Nginx误推非法或重复路径资源;需检查路径是否绝对、无参数、唯一,且仅用于静态文件,动态内容应由后端控制Link头。

HTTP/2 推送(http2_push)配置错误时,浏览器可能重复加载同一资源——不是因为缓存没生效,而是 Nginx 误推了本不该推、或路径不合法的资源,导致浏览器既收到推送帧,又发起常规请求。这类问题隐蔽性强,日志里通常不报错,但会显著拖慢首屏加载、浪费带宽。
确认是否真在重复加载
打开 Chrome DevTools → Network 标签页 → 勾选“Waterfall”和“Protocol”列 → 刷新页面。重点观察:
- 同一资源(如
/static/js/app.js)是否出现两行记录:一行 Protocol 显示h2且 Initiator 是Push,另一行是h2且 Initiator 是Parser或Script; - Push 的那条请求没有“Size”(显示为
(push)),而 Parser 发起的那条有实际字节大小; - 两者时间接近(通常相差几十毫秒),说明浏览器收到了推送但没用上,仍走常规请求。
检查推送路径是否合法且唯一
http2_push 只接受以 / 开头的绝对路径,且不支持查询参数。常见错误包括:
- 写了相对路径:
http2_push static/css/main.css;→ 会被忽略,Nginx 不报错但也不生效; - 带了版本号或 hash 参数:
http2_push /js/bundle.js?v=1.2.3;→ 整个指令被丢弃; - 多个 location 块重复定义同一路径:
location / { http2_push /favicon.ico; }和location ~ \.ico$ { http2_push /favicon.ico; }→ 导致多次推送; - 使用
try_files后路径被重写,但http2_push仍按原始 URI 匹配 → 实际推送了不存在的路径。
解决方法:所有 http2_push 指令必须写在匹配该资源的 location 块内,路径严格为静态文件真实可访问的绝对路径(不含 query),且确保每个资源只被定义一次。
验证推送是否被浏览器接收并复用
仅靠 Network 面板不够可靠。更准确的方式是抓包分析 HTTP/2 帧:
- 用
curl -v --http2 https://your-domain.com/,观察响应头中是否有link: </static/js/app.js>; rel=preload; as=script—— 这表示 Nginx 已将推送转为Link头(需配合http2_push_preload on); - 用 Wireshark + SSLKEYLOGFILE 抓 HTTPS 流量,过滤
http2.type == 0x0(PUSH_PROMISE 帧),确认帧中:path字段值与你期望完全一致; - 若看到多个相同
:path的 PUSH_PROMISE,说明配置有重复;若:path是/css/style.css?ts=123,说明参数未被清理,推送无效。
排除动态内容干扰
http2_push 仅在 Nginx 自己返回静态文件时生效。如果资源由 proxy_pass 或 fastcgi_pass 返回,该指令完全不触发——此时重复加载往往源于后端服务自己加了 Link 头,而 Nginx 又未配置 http2_push_preload off,造成双重 preload。
- 检查后端响应头:
curl -I https://your-domain.com/api/data.json,看是否含Link:; - 若后端已主动推送,Nginx 配置中应禁用自动转换:
http2_push_preload off;; - 对 PHP-FPM 等动态场景,统一由应用层控制
Link头,Nginx 侧不设http2_push。


















