HTTP/2 服务器推送允许服务器在客户端请求之前主动发送相关资源,例如 CSS、JavaScript 和图片。这种机制可以节省浏览器解析 HTML 后再发起请求的往返时间,但同时也带来了重复推送的隐患。例如,一个 HTML 页面内多次引用同一个脚本,或者浏览器已经缓存了该资源,但服务器仍然推送,就会浪费带宽。Nginx 为了解决这个问题,在 HTTP/2 模块中加入了推送日记(push diary)功能。它会在推送每个资源后,把该资源的 URI 记录在内存中,并在同一连接上后续遇到相同 URI 时跳过推送。而控制这些记录能存活多久的,正是 http2_push_diary_timeout 指令。

一、HTTP/2 Server Push 与 push diary 的协作机制
Nginx 从 1.13.9 版本开始支持 HTTP/2 Server Push,主要通过两种方式触发:一种是使用 http2_push 指令手动指定要推送的资源,另一种是配合 http2_push_preload 指令,让 Nginx 自动读取响应头中 Link 字段里的预加载资源信息。无论采用哪种方式,服务器推送的资源都是在主响应发送之前或同时发送到客户端。这种提前发送虽然能加速页面渲染,但如果服务器没有判断资源是否已经被推送过,就会在同一连接上产生重复推送。比如一个页面包含两个相同的样式表引用,Nginx 如果只根据 Link 头去推送,可能会推送两次相同的 CSS 文件。
push diary 机制正是为了避免这种情况而设计。它内部维护一个基于哈希表的记录集合,每个条目保存了资源的 URI 和创建时间。当 Nginx 准备推送某个资源时,会先检查这个资源是否已经存在于 push diary 中。如果存在并且还没有超过 http2_push_diary_timeout 设定的超时时间,Nginx 就会跳过这次推送;如果不存在或者条目已经过期,Nginx 才会执行推送并把新的 URI 记录到 diary 中。因此,http2_push_diary_timeout 实际上控制的是去重窗口的宽度。这个窗口越大,同一连接上相同资源被重复推送的可能性越低,但同时也意味着这些记录占用的内存时间更长。
下面是一个基础配置示例:
http {
server {
listen 443 ssl http2;
server_name ippipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
http2_push_preload on;
http2_push /css/main.css;
http2_push /js/app.js;
http2_push_diary_timeout 60s;
}
}
}
在这个配置中,http2_push 指定了两个固定推送的静态资源,而 http2_push_preload on 允许 Nginx 根据上游响应中的 Link 头自动推送其他资源。http2_push_diary_timeout 60s 设置了记录的超时时间为 60 秒。
二、http2_push_diary_timeout 指令的语法与参数解析
http2_push_diary_timeout 指令的语法非常简单:http2_push_diary_timeout time;。它可以在 http、server、location 三个层级中配置,默认值是 60 秒。该指令从 Nginx 1.25.1 版本开始引入,但需要注意不要与 http2_push 本身的可用版本混淆。超时时间的单位可以是 s(秒)、m(分钟)、h(小时)等,例如 30s、2m、1h 都是合法的。如果设置为 0,则表示禁用 push diary 的超时机制,记录会一直保留直到连接关闭。当然,实际使用中不建议这样配置,因为长期保留记录会持续占用内存。
这个参数的核心作用是平衡重复推送和内存占用。当超时时间设置得很短时,例如 5 秒,那么同一连接上如果用户在 5 秒后又触发了一次对相同资源的推送请求(比如页面局部刷新),Nginx 就会再次推送该资源。如果资源没有变化,这就是一次冗余传输。但当资源更新频繁时,短超时可以保证客户端能更快收到新版本。反之,如果超时时间设置得很长,例如 10 分钟,那么在 10 分钟内同一资源只会被推送一次,即使该资源已经在服务器端更新了,客户端在同一连接上也不会收到新的推送,只能等连接重置或者超时结束。
另一个需要关注的点是,push diary 的记录是绑定在单个 HTTP/2 连接上的。也就是说,不同的连接之间不会共享这些记录。如果客户端断开连接后重新建立连接,之前记录的内容会自动清空。因此,http2_push_diary_timeout 只影响同一条连接上的去重效果,并不能替代浏览器缓存或者服务器端的缓存策略。在配置时需要结合资源的缓存控制头(Cache-Control)来设置合理的超时时间。
下面展示一个针对不同资源类型做分区配置的例子:
http {
server {
listen 443 ssl http2;
server_name ippipp.com;
location /static/ {
# 静态资源长期缓存,推送日记超时时间可以设置较长
http2_push_diary_timeout 10m;
http2_push_preload on;
}
location /api/ {
# 动态接口推送内容变化快,使用较短超时
http2_push_diary_timeout 15s;
http2_push_preload on;
}
}
}
这个配置展示了如何对不同路径设置不同的超时策略。静态资源目录 /static/ 下的文件变更频率低,可以设置 10 分钟;而 /api/ 目录下的动态内容变化较快,设置 15 秒能让后续请求更快地获得新推送。需要注意的是,http2_push_diary_timeout 虽然可以在 location 级别单独设置,但它仍然只对当前连接内已经推送过的 URI 生效,不会影响其他连接。
三、超时配置的调优思路与常见问题
调优 http2_push_diary_timeout 的第一步是明确资源的变更频率和缓存策略。对于带有强缓存(例如 Cache-Control: max-age=31536000, immutable)的静态资源,客户端在缓存有效期内根本不会重新请求,即使服务器推送了也不会被使用。在这种情况下,push diary 的超时时间可以设置得较长,比如 30 分钟甚至 1 小时,因为资源的 URL 通常带有版本号或内容哈希,内容更新时会生成新的 URI,旧的 URI 即使保留在 diary 中也不会影响新资源的推送。
而对于没有缓存控制或者缓存时间很短的动态资源,超时时间应该设置得短一些。例如一个 API 返回的 JSON 数据,如果客户端在一次页面加载中多次触发了相同的请求,服务器第一次推送后,后续请求如果间隔超过超时时间,就会再次推送。如果这个 JSON 数据在短时间内发生了变化,短超时反而有助于客户端拿到最新内容。但如果超时设置得过短,比如 1 秒,那么在页面初始化阶段多个相同资源请求可能因为间隔超过 1 秒而被重复推送,造成浪费。所以一般建议从默认的 60 秒开始,根据实际请求日志和推送频率进行调整。
调试过程中,可以使用 Nginx 的 error_log 配合 HTTP/2 的调试级别来观察推送行为。也可以通过浏览器的开发者工具查看 Network 面板中是否出现重复的 Push 资源。如果发现同一个资源在短时间内被推送了多次,说明超时设置可能过短;如果资源已经更新但客户端长时间没有收到新推送,则说明超时时间过长。需要注意的是,Nginx 的 push diary 不会记录跨连接的推送,所以每次新连接都可能重新推送一遍资源。这是正常行为,不属于重复推送问题。
还有一个常见误区是认为 http2_push_diary_timeout 可以控制推送资源本身的缓存寿命。实际上,它只控制 diary 记录的去重时间,并不会影响客户端对推送资源的存储策略。客户端是否缓存推送资源,仍然取决于响应头中的 Cache-Control、Expires 等字段。因此,在配置 HTTP/2 推送时,应该同时为推送的资源设置合理的缓存头,否则即使服务器推送了,客户端也可能没有缓存,导致下次还需要重新请求。
最后,建议在启用 HTTP/2 Push 之前做好性能测试。并不是所有场景都适合使用服务器推送。如果页面依赖的资源很多,盲目推送可能会占用大量带宽并增加服务器内存开销。可以结合 http2_max_concurrent_pushes 指令限制并发推送数量,再通过 http2_push_diary_timeout 控制去重窗口,两者相互配合才能发挥 HTTP/2 推送的最大价值。
Nginx HTTP/2http2_push_diary_timeout服务器推送修改时间:2026-08-20 11:37:37