usr1信号专用于日志文件重打开,不触发配置重载或进程重启,仅按配置路径重新open日志文件并关闭旧fd,确保旧日志可安全归档、新日志独立写入,操作简单可靠。

官方推荐的方案就是发 USR1 信号让 Nginx 主进程重新打开日志文件,不依赖外部轮转工具,也不需要改配置重启服务。它简单、可靠、兼容所有版本,error_log 和 access_log 都适用。
为什么用 USR1 而不是 HUP 或 RELOAD
HUP 会触发平滑重启(重载配置 + 新旧 worker 交替),而 USR1 只做一件事:按配置里写的 access_log 和 error_log 路径,重新 open() 日志文件,并关闭旧 fd。没有 reload 配置、不中断请求、不 fork 新进程——纯粹的日志 reopen。
- 发
HUP或执行nginx -s reload会 reload 配置,可能意外引入错误或导致连接抖动 -
USR1不检查配置语法,只要 pid 文件存在、权限够,就直接操作文件句柄 - 旧日志文件仍可读(Linux 的文件描述符机制决定的),你随时能
gzip或mv它
标准操作步骤(必须按顺序)
关键点在于:先移走日志文件,再发信号,否则 Nginx 会往新文件里追加写,旧文件无法清空。
- 确认主进程 pid 文件路径,比如
/usr/local/nginx/logs/nginx.pid,用cat读出进程号 - 用
mv重命名当前日志,例如:mv access.log access.log.$(date -d 'yesterday' +\%Y-\%m-\%d) - 立即发送
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid) - 此时 Nginx 已创建新的
access.log并开始写入;旧文件不再被写入,可安全处理(压缩、归档、删除)
容易踩的坑
看似三行脚本,但线上出问题基本都栽在这几个地方:
-
nginx.pid路径不对或权限不足(比如脚本用普通用户运行,但 pid 文件属 root)→ 改用sudo或统一用www-data用户管理日志和 pid - 没等
mv完就发USR1→ 旧日志还在被写,新日志也同时写,造成日志错乱 - 日志路径在配置中用了变量(如
$time_iso8601),但没配open_log_file_cache→ 每次写日志都open/close,I/O 压力飙升 - crontab 中没指定
PATH或工作目录 →date命令找不到,或mv目标路径解析错误
真正要注意的不是“怎么切”,而是“切完之后旧文件是否还在被写”——只要确保 mv 和 USR1 之间无延迟、且 Nginx 进程没崩溃,这个机制就稳得一批。其他方案(比如 logrotate)只是把这三步封装得更自动化,底层还是靠 USR1。











