HTTP/2的服务端推送是一项颇具争议的特性,它允许服务器在浏览器明确请求CSS、JS之前,把资源顺着同一条连接提前推过去。Nginx从1.13.9版本开始原生支持这项功能,配置方式非常简单,但很多开发者配完之后根本不知道推送有没有真正生效,客户端到底是接收了推送还是直接发了RST_STREAM拒绝。这就需要借助自定义的日志格式来观察。本文把http2_push和log_format两块配置结合起来讲,帮你把推送行为完整地记录下来。

一、http2_push的基础配置
Nginx提供了两个核心指令:http2_push和http2_push_preload。前者直接指定要推送的资源路径,后者则解析响应头中Link字段的preload提示并自动转为推送。两者可以并存,但更推荐用preload方式,因为它的语义是标准化的,客户端不支持HTTP/2推送时也能退化为预加载。
一个典型的server配置如下:
server {
listen 443 ssl http2;
server_name example.ipipp.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
root /var/www/html;
location = /index.html {
http2_push /style.css;
http2_push /app.js;
http2_push_preload on;
}
}需要注意,http2_push只能出现在location上下文中,且推送的资源路径必须是URI形式。Nginx会在发送主资源响应的同时,通过PUSH_PROMISE帧告知客户端即将推送的内容。如果客户端不想要这些资源,会立刻发送RST_STREAM取消,但此时服务器可能已经读取了文件甚至占用了带宽,这就是推送被滥用后反而降低性能的原因。
二、用log_format记录HTTP/2推送行为
Nginx内置了两个与HTTP/2直接相关的变量:$http2表示协商出的HTTP协议版本(值为2表示h2,空表示HTTP/1.1),$http2_pushed则标记当前请求是否由服务端推送产生,值为on表示这是一个被推送出去的资源请求。利用这两个变量,我们可以在http块中定义专门的日志格式:
http {
log_format push_diary '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'proto=$http2 pushed=$http2_pushed '
'referer="$http_referer" '
'ua="$http_user_agent" '
'rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/push_diary.log push_diary;
}这样配置之后,每次访问index.html产生的日志里,被推送的style.css和app.js行会出现pushed=on,而未被推送的普通请求则显示pushed=-。通过简单的grep统计就能算出推送命中率:
grep 'pushed=on' /var/log/nginx/push_diary.log | wc -l
grep 'proto=2' /var/log/nginx/push_diary.log | awk '{print $9}' | sort | uniq -c如果想进一步区分推送是否被客户端接受,可以结合$status观察:正常完成的推送返回200,而被RST_STREAM中断的推送在部分版本中会表现为499或其他非标准状态码。虽然Nginx没有直接暴露客户端取消推送的专用变量,但结合推送日志与浏览器DevTools的Network面板交叉验证,基本可以还原完整的推送链路。
三、按条件输出推送日志与常见坑
生产环境中不建议让所有请求都写入push_diary格式,可以用map配合条件日志降低开销。例如只对推送产生的请求单独记录:
map $http2_pushed $push_log_flag {
default 0;
on 1;
}
server {
listen 443 ssl http2;
access_log /var/log/nginx/push_diary.log push_diary if=$push_log_flag;
location / {
http2_push_preload on;
root /var/www/html;
}
}这里用到了access_log的if参数,只有变量值为非空且不为0时才写日志,非常适合推送量大的站点做采样分析。
最后提醒几个实践中的注意点。第一,Chrome从106版本起已默认禁用HTTP/2 Push,如果你的主要用户是Chrome,推送基本不会命中,此时应改用103 Early Hints或Preload头,nginx 1.21.4以上可通过early_hints指令支持前者。第二,推送资源必须是同源的,且不能是已经缓存的资源,否则客户端会直接取消,白白浪费带宽。第三,日志变量$upstream_response_time在纯静态推送场景下为空,不要在告警脚本里依赖它做判断。把推送日志跑上一周,统计出真实的命中率,再决定是保留、调整还是彻底下线http2_push,这才是用数据驱动配置优化的正确姿势。
Nginxhttp2_pushlog_format修改时间:2026-09-05 05:28:37