HTTP/2 Server Push曾经被寄予厚望,很多团队兴冲冲地在Nginx里配置了push指令,结果一查访问日志,发现大量请求根本没推送出去,反而是记了一条带drop的日志。这条日志的来源就是Nginx的push diary机制。理解这套机制,才能判断推送被丢弃到底是正常行为还是配置问题。

push diary到底是个什么东西
push diary直译过来就是推送日记本,它记录的是当前这条HTTP/2连接上,服务端已经向客户端推送过哪些资源。每条记录本质上是一段根据推送资源的URL和请求头计算出来的指纹(Nginx内部用的是64位的hash值)。当Nginx准备推送某个资源时,会先翻一遍这本日记:如果指纹已经存在,说明这条连接上早就推过了,再推一次就是纯粹的带宽浪费,于是直接放弃推送,同时在错误日志里留下一条记录,大意是资源在推送日记中已被找到,推送被丢弃。
注意这个diary是按连接隔离的,不是全局共享的。客户端新开一条TCP连接建立HTTP/2会话,diary就是一张白纸。所以你经常会观察到同一个资源在旧连接上被丢弃、在新连接上又被正常推送,这不是bug,是设计如此。HTTP/2本身没有跨连接的推送去重能力,后来HTTP/3里的服务器推送同样没有解决这一点,这也是Server Push最终在浏览器侧被逐步放弃的原因之一。
可以用一个简单的配置来触发推送,观察日志行为:
location = /index.html {
http2_push /style.css;
http2_push /app.js;
}第一次访问时两个资源会被推送,刷新页面后如果复用同一条连接,日志里就会出现drop记录。如果你把错误日志级别调到info,还能看到更详细的判定过程,方便教学和排查。
日志里出现drop的几种典型场景
第一种也是最常见的一种:客户端主动拒绝了推送。现代浏览器(Chrome、Firefox均已如此)要么彻底移除了对Server Push的支持,要么在SETTINGS帧里把推送并发数设为零。Nginx收到这类信号后,所有push指令都会静默失效,日志中的表现就是推送被跳过或丢弃。很多团队在老浏览器上测试一切正常,换了新浏览器就全军覆没,原因就在这里。
第二种是diary容量限制。Nginx用http2_push_diary_size?并不存在这个指令,实际上diary的容量是编译期固定的(默认256个条目左右),超容量后会按哈希槽覆盖旧条目。极端情况下,页面推送资源特别多时,旧指纹被挤出日记本,可能导致同一资源在一条连接上被推送两次。遇到这种问题,正确做法是精简推送列表,而不是想办法扩容。
第三种是条件请求的影响。如果客户端带着ETag或Last-Modified发起了条件请求,或者资源命中了协商缓存返回304,Nginx也不会推送响应体,日志同样可能出现drop字样。排查时可以用curl的http2支持手动验证:
# 强制使用HTTP/2并打印推送情况 curl --http2 -v https://www.ipipp.com/index.html -o /dev/null 2>&1 | grep -i push
如果curl端能看到PUSH_PROMISE帧而浏览器端看不到,基本可以断定是浏览器侧关闭了推送,跟服务端配置无关。
该不该继续用Server Push:一份务实建议
从工程实践看,Server Push的收益场景已经非常窄了。推送静态资源会被浏览器缓存机制打脸,推送动态内容又难以判断客户端是否已有缓存,加上主流浏览器陆续撤掉支持,今天再投入精力优化push配置,性价比很低。对于仍然在维护老系统、客户端是自研App或嵌入式HTTP/2实现的场景,推送才有一定价值。
如果确实要用,遵循三条原则:只推送首屏关键资源,数量控制在三五个以内;确保推送资源不带有Vary这类影响缓存判断的头;定期用日志统计drop比例,如果丢弃率超过一半,说明推送列表和真实访问模式不匹配,应该收缩范围。可以配合日志分析做统计:
# 统计推送丢弃在错误日志中的占比 grep -c "push diary" /var/log/nginx/error.log grep -c "dropped" /var/log/nginx/error.log
对新项目来说,更稳妥的替代方案是preload链接提示加CDN边缘缓存,让客户端自己决定要不要拉取资源。这套组合没有推送的带宽浪费问题,兼容性也远好于Server Push。理解drop日志背后的机制,不仅能解决眼下的疑惑,也能帮你在做技术选型时看清HTTP/2各项特性的真实边界。
Nginxhttp2_push日志丢弃修改时间:2026-09-10 12:30:27