HTTP/2 的 Server Push 允许服务器在客户端请求 HTML 时主动推送关联的 CSS、JavaScript 等静态资源,省去一轮浏览器解析后的请求往返。Nginx 从 1.13.9 版本开始支持通过 http2_push 指令配合 Link 响应头实现这一能力。不过开启 push 后,很多项目会遇到资源重复推送、依赖遗漏或者推送无效的问题。单纯查看 access.log 只能看到请求数量变化,无法了解 Nginx 内部是如何决定推送哪些文件的。此时需要打开 Nginx 的 debug 级别日志,并理解 http2 模块日志掩码的含义。

HTTP/2 Server Push 的核心配置
Nginx 中对 Server Push 的配置主要有两种方式。第一种是在 server 或 location 块中手动指定要推送的文件,使用 http2_push 指令,参数为 URI 路径。例如:
server {
listen 443 ssl http2;
server_name ipipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
http2_push /assets/app.css;
http2_push /assets/app.js;
root /var/www/html;
}
}
这种配置简单直接,但每增加一个资源就要修改一次 Nginx 配置,维护成本较高。更灵活的方式是使用 http2_push_preload 指令开启自动预加载。当 Nginx 在响应头中看到 Link: </assets/app.css>; rel=preload; as=style 这样的 Link 提示时,会自动将该资源作为 push promise 发送给客户端。上游应用或后端语言可以在返回 HTML 时动态生成 Link 头,Nginx 无需关心具体文件列表。
不管是手动配置还是自动预加载,判断 push 是否成功、推送内容是否合理,都离不开日志。Nginx 默认的 error_log 级别通常为 warn 或 error,不会输出 HTTP/2 模块的详细信息。即使把级别调整为 info,也看不到 push 决策过程。只有进入 debug 级别,模块内部的调试信息才会被记录下来,而这些信息通常带有日志掩码标识。
日志掩码与 debug_connection 的关系
Nginx 的调试日志体系由一个位掩码(mask)来控制哪些模块输出信息。在编译 Nginx 时只要包含 --with-debug 选项,所有模块的调试代码都会参与编译。运行时通过 error_log 指令设置日志级别为 debug 即可启用调试输出。但 debug 级别会产生海量日志,尤其是生产环境高并发时。为了避免全量开启造成性能问题和磁盘写满,Nginx 提供了 debug_connection 指令,只对指定 IP 或网段的连接开启 debug 日志。
配置方法如下:
events {
debug_connection 192.168.1.100;
debug_connection 10.0.0.0/8;
}
error_log /var/log/nginx/debug.log debug;
在上面的配置中,客户端地址为 192.168.1.100 或来自 10.0.0.0/8 网段的连接会输出 debug 日志到 debug.log,其他连接仍然按照 error_log 的级别记录。HTTP/2 模块的日志输出会带有类似 [debug] 12345#0: *1 http2 push: ... 的前缀,其中 *1 是连接编号。而日志掩码并不一定显式打印为数字,更常见的是每个模块的输出由系统内部的 mask 过滤。当 debug_connection 命中后,该连接相关模块的 mask 全部打开,从而可以看到 push 相关的调试信息。
Nginx 源码中 ngx_http_v2_module 的调试日志按事件类型设置了不同的 mask 位。例如帧接收、流状态变化、push 队列处理等。虽然最终日志里通常不会直接出现 mask 数值,但理解这种掩码机制有助于判断为什么只有某些 debug 信息会被记录。开发者也可以通过给 Nginx 打补丁或修改模块源码,将掩码值输出到日志,以便更精确地控制调试粒度。
解读掩码并定位 Push 问题
开启 debug_connection 后,访问一个页面并观察 debug.log,可以看到类似下面的记录:
2025/04/10 10:15:22 [debug] 12345#0: *1 http2 push uri: /assets/app.css 2025/04/10 10:15:22 [debug] 12345#0: *1 http2 push uri: /assets/app.js 2025/04/10 10:15:22 [debug] 12345#0: *1 http2 push complete 2025/04/10 10:15:22 [debug] 12345#0: *1 http2 send HEADERS frame
实际的 Nginx 版本中,日志文本可能略有差异,但核心信息类似。从日志可以看出 Nginx 依次推送了两个资源并完成了 push 流程。如果某个资源没有被推送,日志中就不会出现对应的 http2 push uri 行;如果出现了重复的推送记录但客户端只请求了一次,可能意味着 Link 头与 http2_push 指令重复指定了同一资源。通过阅读这些掩码过滤后的推关日志,可以快速定位是配置遗漏、响应头未生成,还是 Nginx 编译时未启用 HTTP/2 模块。
另一个常见问题是推送了不可缓存的资源或者跨域资源。例如推送的文件路径错误,Nginx 会返回 404,debug 日志会显示 http2 push failed 或类似信息。再比如使用自动预加载时,后端返回的 Link 头中的 URI 使用了绝对地址或包含 <https://ipipp.com/style.css> 这样的形式,Nginx 可能无法正确识别为本地资源,从而忽略推送。此时日志掩码相关的输出能够提示解析失败的原因。
排查时建议先用 curl -I --http2 观察响应头中是否包含 Link,再用浏览器或 curl 实际请求主文档,同时查看 debug.log。如果 http2_push_preload 已开启但日志中没有任何 push 记录,要检查 Nginx 版本是否支持该指令,以及编译参数中是否包含 HTTP/2 和 debug 模块。
实践:搭建最小可调试环境
下面给出一个完整的最小化调试配置,适用于本地或测试服务器。假设已经准备好 SSL 证书,Nginx 编译时包含了 --with-http_v2_module --with-debug。
worker_processes 1;
events {
worker_connections 1024;
debug_connection 127.0.0.1;
}
http {
include mime.types;
default_type application/octet-stream;
error_log /var/log/nginx/debug.log debug;
server {
listen 8443 ssl http2;
server_name localhost;
ssl_certificate /etc/nginx/ssl/localhost.crt;
ssl_certificate_key /etc/nginx/ssl/localhost.key;
location / {
root /var/www/html;
http2_push_preload on;
add_header Link "</style.css>; rel=preload; as=style";
index index.html;
}
}
}
启动 Nginx 后,在本地执行 curl -k https://localhost:8443/,然后查看 /var/log/nginx/debug.log。如果出现 http2 push uri: /style.css 之类的记录,说明 push 和日志掩码调试链路已经打通。需要特别注意的是,debug_connection 127.0.0.1 只对来自本机的连接生效,如果使用局域网 IP 访问,要相应调整地址。
通过这套环境,可以进一步验证不同配置对 push 行为的影响。例如去掉 add_header Link 这一行,观察日志中 push 记录是否消失;或者将 http2_push_preload off,再查看日志是否完全没有 push 相关信息。这样反复对比,就能对 Nginx HTTP/2 push 的日志掩码机制建立直观认识,在真实项目中遇到推送异常时也能迅速定位问题。
总结来说,Nginx 的 http2_push 日志掩码并非一个单独的用户可配置项,而是 debug 日志体系的组成部分。借助 error_log debug 与 debug_connection,开发者可以只对需要的连接输出 HTTP/2 push 的调试细节。理解这套掩码机制,是高效排查 Server Push 问题的基础。
Nginx HTTP/2Server Push日志掩码修改时间:2026-10-03 04:14:11