Server Push是HTTP/2协议中一个颇具争议的特性,Nginx从1.13.9版本开始原生支持,通过http2_push指令可以把指定资源主动推送给浏览器。但推送机制有一个绕不开的问题:浏览器可能已经缓存了该资源,服务端却还在傻傻地推。为了解决重复推送问题,Nginx引入了推送日记(push diary)机制,而http2_push_diary_filter正是控制哪些请求会被记录进日记、哪些请求会触发匹配判断的关键过滤规则。理解它的匹配逻辑,是精细化控制Server Push的前提。

推送日记的工作原理
推送日记本质上是服务端维护的一张哈希表,用来记录已经推送过(或者客户端声明已拥有)的资源指纹。Nginx在计算指纹时,会对资源的请求路径和Host头部做MD5运算,取其128位结果作为键。当客户端发起一个新请求时,Nginx会先查询日记中是否已存在相同的哈希值,如果命中则跳过推送动作,避免带宽浪费。
客户端这边也有对应的配合机制。当浏览器收到推送的资源后,会在后续请求的Cache-Digest头部中携带一份本地缓存摘要(这个机制在Chrome中以Accept-CH相关实验特性存在过)。Nginx收到该头部后会解析出客户端已拥有的资源列表,写入推送日记。这样即使服务端重启、日记清空,也能借助客户端摘要快速恢复判断依据。
需要注意,推送日记的容量是有限的。Nginx默认为每个HTTP/2连接维护固定大小的日记空间,采用简单的截断策略——新条目覆盖旧条目。这也是为什么过滤规则很重要:把无关请求排除在日记之外,可以让有限的日记空间只记录真正需要跟踪的资源。
http2_push_diary_filter的匹配语法与配置示例
http2_push_diary_filter支持两种匹配方式:精确字符串和正则表达式。当参数以~开头时表示大小写敏感的正则,~*表示忽略大小写的正则,否则按普通字符串处理。它的作用是定义一个过滤器,凡是匹配该规则的请求,其资源指纹才会被写入推送日记;换一个角度看,也可以用它来排除某些路径。来看一个完整配置:
server {
listen 443 ssl http2;
server_name example.ipipp.com;
# 推送首页依赖的关键资源
http2_push /static/css/main.css;
http2_push /static/js/app.js;
# 只跟踪静态资源的推送日记,动态接口不记录
http2_push_diary_filter ~*^/static/;
# 或者反向思路:排除不需要跟踪的路径
# http2_push_diary_filter !~*^/api/;
location / {
root /data/www;
index index.html;
}
}上面的配置中,~*^/static/表示所有以/static/开头的请求URI(不区分大小写)都会参与日记记录。当浏览器第二次请求页面时,如果main.css已经在日记中,Nginx就不会重复推送。配置正则时要注意Nginx的PCRE语法,大括号{ }在配置文件中有特殊含义,如果正则里出现量词如{2,5},必须给整个正则加双引号,否则Nginx启动时会报配置解析错误。
另一个容易踩的坑是规则的作用域。http2_push_diary_filter可以放在server、location级别,遵循Nginx标准的配置继承规则:子层级定义后会完全覆盖父层级,而不是叠加。如果你在server层定义了一条过滤规则,又在某个location里定义了另一条,那么该location下只有自己的规则生效。不少人以为规则会合并生效,结果发现部分资源没有被记录进日记,排查半天才发现是继承覆盖问题。
三种推送控制方式的对比与选择建议
Nginx一共提供了三种控制Server Push的方式,各有适用场景。http2_push是静态声明,直接在配置里写死要推送的路径,简单直接但不灵活;http2_push_preload是动态方式,开启后Nginx会扫描响应中的Link头部,自动把带preload标记的资源推送出去,适合与后端应用配合,由应用层决定推送什么;而http2_push_diary_filter不决定推什么,只决定记什么,属于去重层面的控制。
| 指令 | 作用层面 | 典型场景 |
|---|---|---|
| http2_push | 决定推送内容 | 静态页面资源固定依赖 |
| http2_push_preload | 决定推送内容 | 后端通过Link头部动态控制 |
| http2_push_diary_filter | 控制日记记录范围 | 精细化去重、节省日记空间 |
从实际运维经验看,三者的合理组合是:http2_push_preload负责推送决策,由应用层输出Link头部;http2_push_diary_filter限定日记只跟踪静态资源,避免API请求污染日记;对于完全静态的站点则直接用http2_push列出资源清单。验证配置是否生效可以用curl --http2 -I配合-H 'Cache-Digest: ...'模拟客户端摘要,观察PUSH_PROMISE帧是否被抑制。
最后提醒一点,Server Push在Chrome 106之后已被移除支持,Firefox也逐步跟进弃用。如果你的用户群体主要使用现代浏览器,投入大量精力调优推送规则的意义已经不大,更稳妥的方案是回归preload提示加强缓存策略。但如果你维护的是内网系统、App内嵌WebView或者使用支持HTTP/2 Push的老旧客户端,http2_push_diary_filter依然是控制重复推送、节省带宽的有效手段,值得花时间把过滤规则配置精确。
Nginxhttp2_push_diary_filterServer Push修改时间:2026-09-12 17:26:41