网页加载慢的一个典型原因是资源发现太慢:浏览器必须先下载HTML并解析,才知道要请求哪些CSS、JS和字体文件。每多一轮往返,首屏时间就增加几十甚至上百毫秒。HTTP/2的服务器推送(Server Push)正是为了解决这个问题而设计的,它允许服务器在客户端请求HTML时,主动把后续需要的资源一起发过去。本文以Nginx为例,讲清楚如何配置和使用这项能力。

开启HTTP/2推送的前提条件
要使用http2_push指令,Nginx版本必须在1.13.9及以上,并且编译时带上了http_v2模块。可以使用nginx -V命令查看当前版本和编译参数,输出中若包含with-http_v2_module字样,说明模块已经就绪。如果是从发行版仓库安装的Nginx,一般都默认包含该模块;自己编译的话需要在configure时显式加上这个参数。
另一个硬性条件是HTTPS。虽然HTTP/2规范理论上允许明文传输(h2c),但主流浏览器只支持基于TLS的HTTP/2,所以你必须为站点配置有效的SSL证书。证书可以从Let's Encrypt免费获取,也可以购买商业证书。在server块中监听443端口并启用ssl后,再用listen 443 ssl http2;声明协议即可。注意较新的Nginx版本写法有所变化,http2参数被移到了独立的http2 on;指令中,配置时要对照自己版本的官方文档。
server {
listen 443 ssl;
http2 on;
server_name bbccb.com;
ssl_certificate /etc/nginx/ssl/bbccb.com.pem;
ssl_certificate_key /etc/nginx/ssl/bbccb.com.key;
root /var/www/html;
index index.html;
}配置完成后,先用curl -I --http2 https://你的域名验证协议是否协商成功,响应头中出现HTTP/2 200才说明基础环境没问题。这一步不要跳过,很多推送不生效的问题,根源其实是HTTP/2本身就没启用成功。
使用http2_push指令推送关键资源
环境就绪后,推送资源的写法非常直接:在返回HTML的location块中,用http2_push指令指定要推送的文件路径。路径是相对于站点根目录的URI。典型做法是把首屏必需的样式表、主脚本和关键字体推给客户端,让浏览器在解析HTML之前就拥有这些文件。
location = /index.html {
http2_push /css/main.css;
http2_push /js/app.js;
http2_push /fonts/roboto.woff2;
}注意http2_push指令只能出现在location、if或limit_except上下文中,不能写在server块全局。如果首页由后端PHP或Node服务生成,可以把指令放在代理该请求的location里,效果相同。推送的资源数量要克制,一般三到五个以内。每个被推送的文件都占用一条HTTP/2流并消耗带宽,如果推了一大堆浏览器根本用不到的资源,反而会挤占关键资源的带宽,让首屏更慢。
验证推送是否生效,可以打开浏览器开发者工具的Network面板,勾选Hide data URLs以外的过滤器,被推送的资源在Initiator列会显示为Push。也可以用curl加--http2和-nghttp2类工具观察响应帧。如果资源显示的是正常请求而非推送,先检查路径是否正确、location是否真的命中了HTML请求。
用Link响应头实现推送与预加载联动
手动在Nginx配置里罗列资源固然直观,但页面多了以后维护成本很高,前端改版、后端配置不同步都会出问题。更灵活的方案是由应用层在响应头里输出Link头,格式为Link: </css/main.css>; rel=preload; as=style,再让Nginx根据这个头自动决定推送。Nginx官方的ngx_http_v2_module支持http2_push_preload on;指令来开启这一行为。
location = /index.html {
http2_push_preload on;
proxy_pass http://127.0.0.1:8080;
# 后端返回的响应头中携带:
# Link: </css/main.css>; rel=preload; as=style
}这种方式的好处是推送决策回到了掌握业务逻辑的应用代码手中,页面需要什么资源就输出什么Link头,Nginx只负责机械地执行推送,职责分离更清晰。需要注意as属性很重要,它告诉浏览器资源的类型,正确的as值能避免资源被重复拉取或优先级判断错误。常见的对应关系是样式用style、脚本用script、字体用font、图片用image。
这里必须厘清一个容易混淆的概念:rel=preload本身就是标准的预加载机制,浏览器遇到它会提前发起请求;而配合http2_push_preload后,行为升级为服务器直接推送。两者对用户来说结果都是资源提前到达,但推送省掉了一次请求往返。而在HTTP/3和新兴的103 Early Hints方案逐渐普及的背景下,很多团队也开始重新评估推送的必要性,这属于架构层面的取舍。
避免重复推送:结合Cookie与缓存判断
服务器推送最大的坑是缓存问题。浏览器第一次访问时推送很有价值,但第二次访问时这些资源已经在本地缓存里了,服务器不知道这一点,仍然把文件推一遍,白白浪费带宽和用户流量。因此生产环境必须做推送去重,最常见的方案是利用Cookie记录用户是否已经收到过推送。
思路是:前端在首次加载后用JavaScript写一个标识Cookie,比如visited=1;Nginx用map指令读取这个Cookie,如果存在就跳过推送,不存在才执行。
map $http_cookie $push_enabled {
default 1;
~*visited=1 0;
}
server {
location = /index.html {
if ($push_enabled) {
http2_push /css/main.css;
http2_push /js/app.js;
}
add_header Set-Cookie "visited=1; Path=/; Max-Age=86400";
}
}这个方案简单有效,但依赖前端正确写入Cookie,且Cookie被清除后会再次触发推送,这是可以接受的行为。另外要提醒的是,推送的资源最好也设置合理的Cache-Control头,比如带版本号的静态资源可以配置长缓存,这样用户后续访问完全走本地缓存,推送与否都无影响。
最后一个实战建议:推送不是万能药,它最适合首屏关键路径上体积不大、命中率高的资源,比如关键CSS和品牌字体。对于图片列表、可延迟加载的脚本,preload和懒加载往往比推送更合适。上线前务必用Lighthouse或WebPageTest对比开启前后的首屏指标,用数据说话,而不是为了用新特性而用新特性。
NginxHTTP2 Push服务器主动推送修改时间:2026-09-15 08:34:23