导读:本期聚焦于越南程序员创作的《Nginx HTTP/2 Server Push的push diary是什么?随机淘汰机制详解与配置实践》,敬请观看详情。为什么开启HTTP/2 Server Push后,同一个资源有的请求会被推送、有的却不会?这背后其实和push diary内存淘汰机制有关。本文围绕Nginx的http2_push指令展开,先讲清楚push diary记录推送历史的工作原理,再分析其容量上限与随机淘汰策略对推送命中率的影响,接着给出完整的Nginx配置示例和常见踩坑点,包括重复推送带来的带宽浪费、客户端RST_STREAM导致的推送失效等问题,最后说明Nginx新版本移除push功能的背景以及替代方案。如果你正在做首屏资源预加载优化,这篇文章能帮你彻底搞懂推送去重的底层逻辑。

HTTP/2 Server Push曾经是Nginx里备受关注的性能优化特性,它允许服务端在响应HTML之前主动把CSS、JS等关键资源推送给浏览器。但不少人在实测中发现一个奇怪现象:同样的页面,第一次访问时资源被推下来了,刷新几次之后推送却消失了,过一会儿又恢复了。这个现象的根源,在于Nginx内部维护的一个推送记录结构——push diary,以及它容量满了之后的淘汰策略。理解这套机制,是正确使用http2_push指令、避免带宽浪费的前提。

Nginx HTTP/2 Server Push的push diary是什么?随机淘汰机制详解与配置实践

push diary到底是什么,它为什么存在

push diary可以理解为Nginx为每个HTTP/2连接维护的一份推送历史账本。它存在的目的只有一个:防止对同一个资源重复推送。HTTP/2协议本身不会替服务端做这件事,如果浏览器已经缓存了某个CSS文件,服务端仍然一股脑地推送,客户端只能发送RST_STREAM帧拒绝,这不仅浪费了已经发出的带宽,还占用了并发流配额,严重时反而比不开Push更慢。

在Nginx的实现中,每当服务端通过http2_push指令推送一个资源,就会把这个资源的标识(URL经过哈希处理后的指纹)记录到diary里。下一次同一个连接上再遇到推送请求时,Nginx会先查一下这本账:如果指纹已经存在,就跳过推送。这本质上是一种基于哈希指纹的去重机制,和浏览器端的Cache Digest思路一脉相承,只是它记录的是服务端视角的推送历史,而不是客户端的缓存状态。

需要注意的是,diary是连接级别的,不是全局的。一个HTTP/2连接对应一份diary,连接关闭后记录随之消失。所以同一个用户换一条TCP连接(比如网络切换、Nginx端http2_recv_timeout超时断开),推送又会重新开始,这也是为什么测试时现象看起来时好时坏。

容量上限与随机淘汰策略的工作原理

diary不可能无限大,Nginx为它设定了固定的容量上限,这个上限由编译时的常量控制,大约能容纳64条记录,运行期无法通过配置指令调整。容量这么小是合理的:每个条目只是几十字节的哈希指纹,而且一个连接生命周期内真正会被推送的资源通常不多。

关键问题来了:当diary写满之后又有新资源需要记录时,选哪个旧条目让位?Nginx采用的是随机淘汰策略,也就是说,不按插入时间淘汰最旧的,也不统计访问频率淘汰最冷的,而是随机挑一个槽位直接覆盖。这种做法实现极其简单,不需要维护链表或时间戳,内存开销几乎为零,且在条目均匀分布的假设下,每个指纹被淘汰的概率相等,长期来看不会出现系统性的偏差。

但随机淘汰的代价也很明显:它不保证公平。假设你的页面推送了80个资源,而diary只能存64条,那么任意一次新写入都可能覆盖掉一个仍然有效的指纹,导致该资源在下一次请求时被重复推送。最坏情况下,热资源的指纹反复被覆盖,重复推送率会明显高于LRU策略。不过在实践中,单页推送资源数远超64个的场景本身就该反思——推送太多资源说明策略有问题,Nginx官方也一直建议只推送一两个最关键的资源。

还有哈希碰撞的问题。指纹是定长哈希,不同URL理论上可能碰撞,碰撞会让Nginx误以为已经推送过而跳过推送。概率虽低,但如果你观察到某个资源偶尔不推送,除了淘汰,碰撞也是可能的原因之一。

配置示例与实测验证方法

先给出一个典型的Nginx配置。http2_push可以写在location块里,也可以通过响应头Link来动态触发:

server {
    listen 443 ssl http2;
    server_name example.ipipp.com;

    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # 限制并发推送数,防止推送风暴
    http2_max_concurrent_pushes 10;

    location / {
        root /var/www/html;
        index index.html;

        # 静态写法:无条件推送关键CSS
        http2_push /css/critical.css;
    }

    # 动态写法:后端返回 Link 头时自动推送
    location /app/ {
        proxy_pass http://127.0.0.1:8080;
        http2_push_preload on;
    }
}

其中http2_push_preload on会让Nginx识别后端响应中的Link: </app.css>; as=style; rel=preload头并转换为推送。动态方式更灵活,推荐由应用层决定推送什么,Nginx只负责执行。

验证推送是否生效,最直接的工具是Chrome的chrome://net-export或者开发者工具的Network面板(勾选Protocol列,看Push状态),也可以用curl调试:

# curl 需要编译时带 nghttp2 支持
curl -sI --http2 https://example.ipipp.com/ \
  -H 'Accept: */*' -w '%{http_version}\n'

# 观察推送流,可配合 nghttp 客户端
nghttp -ans https://example.ipipp.com/

如果想观察diary的淘汰效果,可以写一个脚本对同一连接连续发起几十次请求(HTTP/2连接复用是前提),统计推送触发的次数。你会发现在资源数超过diary容量后,重复推送开始出现,且出现时机没有规律,这正是随机淘汰的特征。

常见踩坑点与演进方向

第一个坑是盲目推送。push diary只在单连接内去重,浏览器缓存命中时客户端仍会拒绝推送,服务端是感知不到的。所以推送决策应该尽量依赖客户端提示,比如结合cookie判断是否首访,只对新用户推送critical.css,否则RST_STREAM的开销会吃掉你省下的请求延迟。

第二个坑是推送的资源本身很大。HTTP/2的推盛会占用带宽,如果CSS有几百KB,推送反而阻塞了HTML的多路复用,这是协议层面的队头干扰。经验值是推送资源控制在几十KB以内,且数量不超过两三个。

更重要的一点是:Nginx从1.25.1版本起官方移除了HTTP/2 Server Push支持,http2_push等指令已经废弃。官方给出的理由是Chrome等主流浏览器早已禁用了对Push的客户端支持,实际收益趋近于零。如果你在维护老版本Nginx上的Push配置,理解diary机制仍有价值;但新项目应改用Link: rel=preload响应头或103 Early Hints,它们不依赖连接级去重,与浏览器缓存天然协同,是当前更稳妥的首屏资源提示方案。

NginxHTTP/2 Server Pushpush diary修改时间:2026-09-15 06:50:35

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260915/57106.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。