在 Nginx 中启用 HTTP/2 Server Push 后,服务器可以在响应主文档时主动把页面依赖的样式表、脚本甚至图片推送给客户端,省去浏览器解析 HTML 后再发起二次请求的往返时间。不过推送并不是无限缓存的,服务器需要根据资源的更新频率设置合理的有效期,否则客户端要么频繁重新验证,要么长期使用过期文件。标题里的 http2_push_diary_ttl_weeks 看起来像是控制推送日记保留周数的参数,但 Nginx 官方模块里并没有这个指令,它更可能来自某个第三方补丁或业务方自定义的配置项。理解它的核心含义,需要先回到 HTTP/2 Push 的缓存与记录机制。

Nginx 原生提供的 HTTP/2 推送指令集中在 ngx_http_v2_module 模块中,最常用的包括 http2_push、http2_push_preload 和 http2_max_concurrent_pushes。其中 http2_push 用于在 location 或 if 上下文中指定要推送的资源路径,比如 http2_push /css/main.css;。而 http2_push_preload on; 会读取上游或后端响应里的 Link 头,自动推送带有 preload 关系类型的资源。这两种方式都只负责把资源推出去,至于资源在客户端缓存多久,是由响应头里的 Cache-Control 或 Expires 决定的,与推送指令本身没有直接关系。
HTTP/2 Push 为什么会涉及 diary 和 TTL
在 HTTP/2 的实现里,服务器推送一个资源前,必须确认客户端是否已经有该资源的有效缓存。如果客户端已经有了新鲜副本,再推送就是浪费带宽。Nginx 判断是否推送的依据之一是请求头里的缓存协商信息,以及自身维护的推送记录。所谓 diary,可以理解为一个内部维护的推送历史表,用来记录哪些资源在什么时间被推送过、对应的缓存版本是什么。给这个记录设置 TTL 周数,就是为了防止过期记录无限增长,也让服务器在资源更新后能够重新触发推送。
但要注意,Nginx 官方并没有暴露名为 diary_ttl_weeks 的指令。如果你的配置文件中出现 http2_push_diary_ttl_weeks 4; 这样的写法,通常说明使用了定制过的 Nginx 构建版本,或者该参数被映射到了某个内部变量。在标准 Nginx 环境里,要实现类似按周管理推送缓存的效果,需要组合使用 expires 7d;、add_header Cache-Control "public, max-age=604800"; 以及合理的 ETag 或 Last-Modified 机制。下面给出的示例配置就能达到资源缓存一周的目的。
把 TTL 设置为周而不是天,主要是为了适应低频更新的静态资源。比如企业官网的全局样式、图标字体或前端构建产物,通常每周发布一次,缓存 7 天既能减少请求,又不会让用户在发布后长时间看到旧版。如果资源一天多变,把 TTL 设成几周反而会导致更新困难。因此 diary_ttl_weeks 这类参数应该结合资源变更频率来调整。
用标准指令实现按周缓存推送资源
下面是一段可复用的 Nginx 配置示例。它开启 HTTP/2 Push 预加载,并对常见静态资源设置 7 天缓存,相当于用标准能力模拟了按周管理 TTL 的效果。配置中同时保留了推送并发数限制,避免一次推送过多资源影响主文档传输。
server {
listen 443 ssl http2;
server_name ipipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
root /var/www/html;
index index.html;
http2_push_preload on;
http2_max_concurrent_pushes 10;
location = /index.html {
add_header Cache-Control "no-cache";
}
location ~* \.(css|js|woff2|png|jpg|svg)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800";
try_files $uri =404;
}
}
上面的 location 块对 CSS、JS、字体和图片设置了 max-age=604800 秒,也就是 7 天。配合 http2_push_preload on;,当后端或上游在响应 HTML 时返回类似下面的 Link 头,Nginx 就会主动推送对应资源,而且客户端收到推送资源后会根据 Cache-Control 缓存一周,一周内再访问同站不会再发起资源请求。
<link rel="preload" href="/css/main.css" as="style"> <link rel="preload" href="/js/app.js" as="script">
如果你确实在某个定制版本中看到 http2_push_diary_ttl_weeks 参数,通常它接受整数,单位是周。设置为 1 表示推送记录保留一周,设置为 0 可能表示关闭推送记录或者立即过期。具体语义需要查阅该版本的文档。在标准 Nginx 下不要直接写入这个指令,否则启动时会报 unknown directive 错误。遇到这种情况,可以先通过 nginx -V 查看编译参数,确认是否包含额外模块。
对于静态资源推送,还有一点不能忽略:ETag 和 Last-Modified 的配合。仅设置 max-age 虽然能让浏览器在缓存有效期内直接使用本地副本,但如果服务器资源更新了而 URL 未变,客户端在 TTL 内不会重新验证,就会使用旧文件。所以要实现可靠的按周缓存策略,应该在资源内容变化时生成新的文件名哈希,或者允许客户端在缓存过期后通过 ETag 进行 304 协商。
验证推送是否真正生效
配置完成后,仅用 curl 看响应头可能不够,因为 Server Push 是协议栈层面的行为。最直观的方法是用 Chrome DevTools 的 Network 面板,查看资源的 Initiator 列是否显示 Push,或者在 Timing 里看到资源开始时间早于 HTML 解析完成时间。也可以使用支持 HTTP/2 的命令行工具,比如 nghttp -ans https://ipipp.com/index.html,它能直接列出服务器推送的资源。
另一个验证重点是缓存是否按周生效。打开浏览器开发者工具,切换到 Application 或 Network 面板,检查推送资源的 Response Headers 中 Cache-Control: public, max-age=604800 是否已经出现。如果发现资源仍然每次都发请求,可能的原因包括:Nginx 没有正确返回该响应头、浏览器缓存已被清理、或者在 Server Push 发生时没有使用有效的缓存键。可以通过设置更短的 TTL 测试,比如先改成 expires 1m; 验证流程,再恢复到 7 天。
在大型站点中,推送过多资源会抢占主文档带宽,造成负优化。借助 http2_max_concurrent_pushes 限制并发推送数量,同时在 Chrome 里观察是否出现 ERR_SPDY_PROTOCOL_ERROR 或大量 RST_STREAM 帧。如果客户端已经有缓存,服务器理应跳过推送,但部分旧版本实现可能重复推送,这时就可以通过类似 diary 记录或缓存键判断来优化。理解这些细节,比单纯查找一个名为 diary_ttl_weeks 的指令更有价值。

