HTTP/2 Server Push曾经是Nginx比较受关注的一项特性,通过http2_push指令可以主动把CSS、JS等资源推送给浏览器,减少一次往返请求。但很多人在用的时候会遇到一个奇怪的现象:明明服务器上的文件已经更新了,客户端却还在使用旧的资源,抓包看也没有触发新的推送。这背后往往就和push diary(推送日记)机制有关。本文就来聊聊push diary到底是什么、它存在哪里、以及如何通过命令和配置去清除它。

一、什么是http2_push_diary,它为什么会缓存旧资源
push diary本质上不是Nginx磁盘上的一个文件,而是一种会话级别的内存记录。在HTTP/2协议中,服务器向客户端推送资源之前,会先发送一个PUSH_PROMISE帧,声明即将推送的资源URL。客户端收到后会把这个URL记在自己的推送缓存里,如果浏览器随后自己又请求了这个URL,就会直接命中推送缓存,而不会真正发出网络请求。
这里有个关键点需要厘清:真正的push diary是维护在客户端浏览器侧的,Nginx作为服务器并不知道浏览器内部记录了哪些URL。Nginx侧的http2_push和http2_push_preload指令只负责决定"推什么",而浏览器端的推送缓存则决定"要不要重新请求"。所以当你更新了资源文件却发现客户端不生效时,问题通常出在两个地方:一是浏览器推送缓存还留着旧记录,二是资源URL没有变化导致缓存命中策略未失效。
另外,Nginx自身在连接层面也会维护一些流(stream)状态和推送去重逻辑,这些状态保存在worker进程的内存中。如果配置频繁热更新,或者使用了http2_body_preread_size之类的缓冲相关指令,进程内部状态和实际文件之间可能出现短暂的不一致,这也是开发者误以为"存在一个push diary缓存文件"的常见原因。
二、服务端如何强制刷新推送状态:reload与重启命令
虽然push diary主要在浏览器侧,但服务端依然可以通过管理Nginx进程来强制刷新所有连接和内部状态。最常用的命令是平滑重载:
nginx -s reload # 或者指定配置文件路径 /usr/local/nginx/sbin/nginx -s reload -c /usr/local/nginx/conf/nginx.conf
nginx -s reload会向master进程发送SIGHUP信号,master收到后重新读取配置文件,然后优雅地关闭旧的worker进程。旧worker会处理完当前已有的HTTP/2连接再退出,新连接由新worker接管,进程内的流状态、推送队列也随之重建。如果你的推送逻辑出现异常,比如推送了不该推的资源,reload是代价最小的处理手段。
如果怀疑进程内存状态已经脏了,reload不够彻底,可以直接重启:
# 完整停止再启动,彻底清空进程内所有HTTP/2会话状态 nginx -s stop sleep 1 nginx # 查看进程确认启动正常 ps -ef | grep nginx
需要提醒的是,重启会断开所有现存的长连接,包括HTTP/2的多路复用连接,正在传输的请求会被中断。生产环境建议放在低峰期执行,或者前置一层负载均衡做连接排空。对于用systemd管理的系统,systemctl reload nginx和systemctl restart nginx分别对应上述两种操作,效果一致。
三、从配置层面规避推送缓存带来的更新不生效问题
比反复清除缓存更优雅的做法,是从源头让推送资源的URL具备版本感知能力。下面是一个典型的推送配置:
server {
listen 443 ssl http2;
server_name www.ipipp.com;
# 推送静态资源
location = /index.html {
http2_push /style.css;
http2_push /app.js;
}
# 也可以配合Link头批量推送
location / {
http2_push_preload on;
add_header Link "</style.css>; as=style; rel=preload, </app.js>; as=script; rel=preload";
}
}这个配置的问题在于,资源URL固定不变,浏览器推送缓存和磁盘缓存都可能命中旧版本。解决办法是给URL加上指纹参数,比如把/style.css改成带哈希的形式/style.3f9a2c.css,或者在构建阶段生成带版本号的文件名。URL一变,PUSH_PROMISE声明的地址就变了,客户端不会再命中旧的推送记录,自然拉取新文件。
还可以利用变量让推送更灵活,http2_push指令支持变量形式:
location = /index.html {
# 根据变量动态决定推送内容
set $push_asset "/app.$arg_ver.js";
http2_push $push_asset;
}此外要注意,推送是有成本的。客户端如果不需要这些资源,推送反而浪费带宽,所以可以借助map结合Cookie判断是否首次访问,只对首次访问的用户执行推送,回访用户则跳过。这既减轻了服务端压力,也降低了推送缓存被滥用的概率。
四、排查与验证推送是否生效的常用命令
改完配置后,怎么确认推送行为和缓存状态?最直接的方式是看Nginx的调试日志和抓包工具。先开启更详细的错误日志级别:
# 修改nginx.conf error_log /var/log/nginx/error.log debug; # 重载配置 nginx -s reload # 观察日志中的http2相关输出 tail -f /var/log/nginx/error.log | grep -i http2
抓包验证可以用tcpdump或者浏览器开发者工具。在Chrome的Network面板中,推送的资源会在Initiator列标记为Push,或者显示Push/Other,据此可以判断资源是推送到达还是普通请求获取的。命令行下可以这样抓取:
tcpdump -i eth0 -A 'tcp port 443 and host www.ipipp.com' -w http2.pcap # 用wireshark打开分析,过滤PUSH_PROMISE帧 # 显示过滤器:http2.type == 5
最后还有一点值得说明:由于HTTP/2 Server Push在实际使用中命中率不理想、实现复杂度高,Chrome等浏览器已经陆续移除了对推送缓存的支持,Nginx官方在1.25.x之后的版本中也标记了相关指令的弃用趋势,并推荐使用Link头的preload标准预加载方式替代。如果是新项目,直接用103 Early Hints配合preload会更符合长期演进方向;存量配置则可以在做好URL版本化的前提下继续运行,遇到缓存不更新的问题时,按照本文的reload、重启和配置改造思路逐层排查即可。
Nginxhttp2_push_diary缓存清除修改时间:2026-09-08 06:38:43