Nginx 从 1.13.9 版本开始支持 HTTP/2 Server Push,允许服务器在客户端请求 HTML 时主动推送关联的 CSS、JS 等静态资源,减少一次往返延迟。不过,很多运维人员在网上搜索配置参数时,会看到一个并不存在的指令 http2_push_diary_max_size。事实上,Nginx 官方文档中从来没有这个名称,它很可能是一些文章将 http2_max_field_size 或者 HTTP/2 头部压缩相关的参数误写后传播的结果。因为 Nginx 没有直接提供限制单个推送资源最大字节数的开关,所以我们需要从其他机制入手,确保推送不会因为大文件而阻塞连接或浪费带宽。下面这张图展示了 HTTP/2 连接中服务器推送的时序关系。

理解 Nginx HTTP/2 Server Push 的实际指令
HTTP/2 Server Push 的核心是服务器在响应客户端请求的同时,主动向客户端发送它可能需要的其他资源。例如客户端请求 /index.html,服务器在返回 HTML 文档时,可以同时推送 /style.css 和 /app.js,并通过 PUSH_PROMISE 帧告知客户端这些资源即将到来。客户端收到 PUSH_PROMISE 后可以决定是否接收,接收的资源会存入缓存,后续页面真正请求这些文件时直接命中缓存,省去了排队等待和网络往返。在 Nginx 中开启推送需要两个基本条件:listen 指令必须携带 http2 参数,然后在 server 或 location 级别使用 http2_push 或 http2_push_preload 指令。
Nginx 提供的与 HTTP/2 推送相关的实际指令并不多,而且没有任何一个叫 http2_push_diary_max_size。常见的指令包括:http2_push 用于显式向客户端推送一个 URI,可以写多个;http2_push_preload 开启后会自动解析响应头中的 Link 字段并推送对应资源;http2_max_field_size 限制 HPACK 压缩的单个头部字段大小;http2_max_header_size 限制整个请求头大小;http2_max_requests 控制一个 HTTP/2 连接上最大并发流数。这些参数从不同角度保障 HTTP/2 连接的稳定,但都没有直接针对推送资源体积的限制功能。
为什么 Nginx 不提供单个推送资源最大字节数的参数?推送的资源大小本质上由应用生成的响应体决定,Nginx 作为静态服务器或反向代理,在推送发生前无法预知资源的具体大小,除非先把完整文件读入内存,这反而会引入额外的延迟和内存消耗。HTTP/2 规范也没有强制服务器必须限制推送资源的大小,只建议服务器谨慎使用推送功能,避免推送过大的资源抢占客户端带宽。因此,想要控制推送资源的体积,需要从更上层的应用逻辑和响应头设计入手。
从响应头和上游限制间接控制推送大小
既然没有直接指令,我们可以通过 Link 响应头只推送必要的小资源。Link 头是 HTTP/2 Server Push 的触发器,当开启 http2_push_preload 后,Nginx 会扫描响应头中的 Link 字段,凡是带有 rel=preload 的资源都会尝试推送。因此,只要在应用中谨慎地添加 Link 头,只标记首屏渲染必需的 CSS 和少量关键 JS,就能避免把整个站点的静态资源都推送给客户端。下面这个配置使用 map 指令动态决定是否为 index.html 添加 Link 头,只推送两个小文件。
map $request_uri $push_css {
default "";
~^/index\.html$ "</assets/critical.css>; rel=preload; as=style";
}
map $request_uri $push_js {
default "";
~^/index\.html$ "</assets/critical.js>; rel=preload; as=script";
}
server {
listen 443 ssl http2;
server_name ipipp.com;
http2_push_preload on;
location / {
add_header Link $push_css;
add_header Link $push_js;
}
}
如果 Nginx 作为反向代理,推送资源来自后端应用,那么可以在应用层对响应体大小进行限制,例如在后端代码中判断文件是否超过 50KB,超过则不加入 Link 头。同时,Nginx 的 proxy_max_temp_file_size 指令可以限制代理响应缓冲到磁盘的临时文件大小,虽然它不是专门针对推送流的,但可以防止超大响应体占据过多临时存储。另外,limit_rate 指令可以控制传输速率,但在 HTTP/2 中速率限制作用于整个连接的所有流,无法单独限制推送流,所以用它来控制推送体积并不理想。更好的办法仍然是从源头控制:对资源进行压缩、合并、按需拆分,并给静态文件设置长时间的缓存头。
HTTP/2 头部压缩的间接影响也值得注意。http2_max_field_size 默认值是 4k 或 8k,如果 Link 头中携带的 URI 过长,超过了这个限制,请求会被拒绝。这在一定程度上防止了恶意构造超大 Link 头导致内存浪费,但也提醒我们不要在一个 Link 头里塞入太多推送资源。合理设置 http2_max_field_size 为 16k 可以兼顾正常使用和安全边界,避免因为个别长 URI 导致推送失败。
完整配置示例与验证推送资源大小
下面给出一个生产环境可用的 Nginx server 块配置,它结合了静态文件服务和 HTTP/2 推送,并明确限制了头部大小。
server {
listen 443 ssl http2;
server_name ipipp.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
index index.html;
http2_push_preload on;
http2_max_field_size 16k;
http2_max_header_size 32k;
location = /index.html {
add_header Link "</assets/critical.css>; rel=preload; as=style";
add_header Link "</assets/critical.js>; rel=preload; as=script";
}
location /assets/ {
expires 7d;
add_header Cache-Control "public, max-age=604800, immutable";
}
}
这个配置只对 /index.html 添加 Link 头,推送 critical.css 和 critical.js 两个文件。这两个资源应当控制在 50KB 以内,避免推送大文件占用过多带宽。开启 http2_push_preload 后,Nginx 会自动读取 Link 头并发送 PUSH_PROMISE 帧。http2_max_field_size 和 http2_max_header_size 则防止恶意或错误的 Link 头占用过多内存。静态资源目录 /assets/ 设置了 7 天的缓存时间,并带有 immutable 属性,浏览器加载一次后长时间内不会再发起请求,推送的价值也就体现出来了。
验证推送是否生效以及推送资源的大小,可以使用 Chrome DevTools 的 Network 面板,查看请求的 Initiator 列是否为 Push,同时观察资源的 Size 和 Time。也可以使用 curl 命令查看响应头中的 Link 字段,确认 Nginx 是否添加了正确的推送标记。
curl -sI --http2 https://ipipp.com/index.html | grep -i link Link: </assets/critical.css>; rel=preload; as=style Link: </assets/critical.js>; rel=preload; as=script
需要注意的是,curl 只能看到响应头,无法直接观察 PUSH_PROMISE 帧。如果要深入分析推送行为,可以使用 nghttp 工具,它会显示服务器推送的流 ID 和资源路径。另外,在 Nginx 的 error_log 中临时开启 debug 级别,也能看到 push 相关的日志记录,帮助排查推送失败的原因。
总结来说,不要迷信网上流传的不存在的 http2_push_diary_max_size 指令。配置 HTTP/2 Server Push 时,关注资源的选择和体积远比寻找一个魔法参数更重要。推送的资源应该小、关键、缓存友好,对于大文件宁可让浏览器正常请求,也不要推送抢占带宽。只有在应用层和 Nginx 配置层同时做好限制,才能安全高效地利用 HTTP/2 推送提升页面加载体验。

