HTTP/2协议带来的多路复用已经大幅提升了页面资源加载效率,而服务器推送(Server Push)则允许Nginx在浏览器明确请求之前,主动把关键资源推送到客户端,进一步减少往返等待。与此同时,访问日志如果不做切割处理,单文件会越滚越大,排查问题和归档都很麻烦。本文把http2_push配置、按天切割日志、切割后的通知重载这三件事串起来讲,给出一套可以直接落地的完整方案。

一、配置http2_push实现服务器推送
要让Nginx支持HTTP/2推送,首先需要确保监听端口启用了HTTP/2协议,同时申请并配置了HTTPS证书,因为主流浏览器只会在HTTPS上协商HTTP/2。基础配置如下:
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;
location / {
root /var/www/html;
index index.html;
http2_push /style.css;
http2_push /app.js;
}
}上面的写法是静态推送,只要客户端访问该location下的页面,Nginx就会无条件推送指定资源。它的优点是配置简单、行为可预测,缺点是无法根据页面实际内容区分推送对象,容易把客户端已经缓存的资源重复推过去,反而浪费带宽。
更灵活的做法是使用http2_push_preload指令配合Link响应头。应用后端在返回HTML时输出一个Link头,标记哪些资源希望被推送,Nginx检测到这个头后会自动转为push。例如后端返回Link: </app.js>; as=script; rel=preload,Nginx端只需开启http2_push_preload on;。这种方式把决策权交给了业务层,可以做到按页面、按用户状态精准推送。
需要注意一点:从Chrome 106开始,浏览器侧对服务器推送的支持已经被移除,因为它在实际场景中的收益普遍低于预期。如果发现推送不生效,先用curl --http2 -I确认协商是否成功,再检查Nginx版本是否支持相关指令(1.13.9及以上才有http2_push)。对于新项目,更推荐用preload响应头替代推送,指令本身保留也不冲突。
二、利用Nginx变量实现按天切割日志
最简单的按天分文件方案,是直接在access_log中使用内嵌变量拼接日期。Nginx默认支持$time_iso8601变量,配合map可以提取出年月日:
map $time_iso8601 $log_date {
default 'unknown';
'~^(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})' $y$m$d;
}
http {
access_log /var/log/nginx/access_$log_date.log main;
error_log /var/log/nginx/error.log warn;
}这种方法的优点是完全不依赖外部工具,日志文件天然按日期命名,不需要重启Nginx。但它也有一个明显的坑:Nginx为每个日志文件保持打开的文件描述符,文件数量会持续增长,且无法在写入过程中自动压缩或删除旧文件,长期运行必须配合定时清理脚本。
生产环境更主流的做法是使用系统的logrotate。新建/etc/logrotate.d/nginx文件,内容如下:
/var/log/nginx/access.log /var/log/nginx/error.log {
daily
rotate 30
missingok
notifempty
compress
delaycompress
dateext
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}其中daily表示按天切割,rotate 30保留30份历史文件,compress配合delaycompress会把切割出的旧文件压缩,dateext让文件名自动带上日期后缀。关键在于postrotate这段:它就是下一步要说的通知机制。
三、切割后的通知机制与平滑重载
日志被mv改名之后,Nginx进程持有的仍然是旧文件的描述符,继续写入的还是改名后的那个文件,新文件不会被使用。因此切割完成后必须通知Nginx重新打开日志文件。这里有两个信号可以用:USR1会让Nginx重新打开日志文件,HUP则会重新加载整个配置文件。日常日志切割用USR1就够了,它开销极小,不会中断服务;HUP一般留给配置变更时使用。
如果你用的是systemd发行版,也可以直接调用官方封装好的命令,效果等价于发送USR1信号:
# 手动触发一次日志重开 nginx -s reopen # 或者通过systemd单元 systemctl kill -s USR1 nginx.service # 验证日志是否已切换到新文件 ls -l /var/log/nginx/ tail -f /var/log/nginx/access.log
除了系统层面的通知,有些团队还需要在日志切割后触发下游动作,比如把当天日志同步到日志分析平台、发送一份通知邮件等。这些都可以追加在postrotate脚本里,例如追加一行rsync -az /var/log/nginx/access_*.log log@10.0.0.8:/data/nginx/,再通过一个简单的mail命令通知值班人员。注意脚本要保持幂等且执行时间短,否则logrotate超时会影响后续轮转任务。
最后排查问题时可以关注两点:一是logrotate -d /etc/logrotate.d/nginx以dry-run模式检查配置是否正确;二是确认Nginx master进程的pid文件路径与脚本中一致,某些自定义编译安装的环境pid路径可能不在/var/run/nginx.pid,路径不匹配会导致通知信号发不出去,表现为日志切割后新文件始终是空的。把推送配置、日志切割和通知重载这三块理顺之后,Nginx在性能优化和日常运维两个方向都能保持稳定可控。
Nginx配置http2_push日志切割修改时间:2026-09-03 07:12:41