Nginx 运维与监控方案(变量/日志/stub_status)
整理 Nginx 日常运维用到的内置变量、日志格式(含 JSON 化)、stub_status 状态页监控与 gzip 优化点。stub_status 输出为 Ubuntu 22.04 实测结果
Nginx 运维与监控方案(变量/日志/stub_status)
目标:整理 Nginx 日常运维用到的内置变量、日志格式(含 JSON 化)、stub_status 状态页监控与 gzip 优化点。stub_status 输出为 Ubuntu 22.04 实测结果。
环境信息
- 操作系统:Ubuntu 22.04.5 LTS
- Nginx:1.18.0(Ubuntu)
- 访问日志:/var/log/nginx/access.log
- 错误日志:/var/log/nginx/error.log
- 状态页:
stub_status(需--with-http_stub_status_module)
常用内置变量
| 变量 | 含义 |
|---|---|
| $remote_addr | 客户端 IP;反向代理后为代理地址,真实 IP 需用 X-Forwarded-For |
| $remote_user | 已过 Basic Auth 的用户名 |
| $remote_port | 客户端随机端口 |
| $request | 请求方法 + URI + 协议 |
| $request_method | GET/POST/PUT/DELETE |
| $request_uri | 含参数的原始 URI($document_uri?$args) |
| $request_filename | 请求资源在磁盘的绝对路径(root/alias + URI) |
| $request_time | 请求处理耗时(秒,毫秒精度) |
| $args | URL 全部查询参数 |
| $http_referer | 来源页面 |
| $http_user_agent | 客户端 UA |
| $http_x_forwarded_for | 反向代理链客户端 IP 记录 |
| $http_<name> | 任意请求头,小写、横线换下划线 |
| $host | 请求 Host |
| $scheme | http/https |
| $server_addr / $server_name / $server_port | 服务器地址/名/端口 |
| $server_protocol | HTTP/1.0、HTTP/1.1、HTTP/2 |
| $status | 响应状态码 |
| $body_bytes_sent | 响应体字节数(不含头) |
| $bytes_sent | 响应总字节数 |
| $upstream_addr | 实际后端地址 |
| $upstream_response_time | 后端处理耗时 |
| $time_iso8601 / $time_local | 时间戳 |
⚠️ 说明:Nginx 置于负载均衡器/反向代理之后时,后端 Web 拿不到真实客户端 IP,需依赖代理注入的
X-Forwarded-For,$remote_addr得到的是代理 IP。
日志格式配置
text 格式(含 server_name:port)
# log_format 只支持 http 块
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$server_name:$server_port';
access_log /var/log/nginx/access.log main;
JSON 格式(便于日志平台采集)
log_format access_json '{"@timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"clientip":"$remote_addr",'
'"size":$body_bytes_sent,'
'"responsetime":$request_time,'
'"upstreamtime":"$upstream_response_time",'
'"upstreamhost":"$upstream_addr",'
'"http_host":"$host",'
'"uri":"$uri",'
'"xff":"$http_x_forwarded_for",'
'"referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"status":"$status"}';
access_log /var/log/nginx/access_json.log access_json;
实测 JSON 日志样例:
{"@timestamp":"2026-09-27T15:20:35+00:00","host":"127.0.0.1","clientip":"127.0.0.1","size":0,"responsetime":0.000,"upstreamtime":"-","upstreamhost":"-","http_host":"localhost","uri":"/test.html","xff":"-","referer":"-","http_user_agent":"curl/7.81.0","status":"200"}
💡 说明:
access_log <path> <format>必须写在使用该log_format之后;JSON 中字符串字段用引号、数字字段(如 size/responsetime)不加引号。
stub_status 状态页监控(实测)
在 server 块加:
server {
location /status {
stub_status on;
access_log off;
allow 127.0.0.1; # 生产务必限制来源
deny all;
}
}
nginx -t && nginx -s reload 后,curl -s http://localhost/status 实测输出:
Active connections: 1
server accepts handled requests
2 2 2
Reading: 0 Writing: 1 Waiting: 0
指标含义:
| 指标 | 含义 |
|---|---|
| Active connections | 当前活动连接数 |
| accepts | 累计接受连接数 |
| handled | 累计处理连接数 |
| requests | 累计请求数 |
| Reading | 读取请求头中的连接数 |
| Writing | 写响应中的连接数 |
| Waiting | 空闲 keep-alive 连接数(≈活动-读写) |
💡 说明:
handled大于accepts过多可能触发worker_connections上限拒绝;Writing长期过高说明后端响应慢/连接堆积。将该页接入 Prometheus 时数据要转 metrics 格式(可借助 nginx-module-vts 或 exporter)。
访问控制(allow/deny)
按来源 IP 放行/拒绝,常用于 status 页、后台管理路径:
location /status {
stub_status on;
access_log off;
allow 127.0.0.1; # 只允许本机/内网
allow 192.168.1.0/24;
deny all; # 其余拒绝(403)
}
💡 说明:
allow/deny按顺序匹配,先匹配到即生效;白名单用nginx -t && nginx -s reload后生效。
防盗链(valid_referers,实测)
防止图片等资源被其他站点引用、免费蹭带宽。仅允许本站与空来源放行:
location ~* \.(gif|jpg|jpeg|png|svg|webp)$ {
valid_referers none blocked *.example.com example.com;
if ($invalid_referer) { return 403; } # 不合法来源直接 403
root /var/www/html;
}
实测结果(-H "Host: feattest.local"):
| 请求 Referer | 结果 |
|---|---|
本站内来源 http://feattest.local/… | 200(放行) |
外部盗链 http://evil.com/x | 403(拒绝) |
无 Referer(none 命中) | 200(放行) |
⚠️ 说明:
if提取$invalid_referer判断,return在 rewrite 阶段即刻生效,无需额外逻辑;如需完全禁止无 Referer 请求,去掉none即可。
限速 limit_req(实测)
按客户端 IP 限制每秒请求数,防止刷接口,超出返回 503:
# http 块定义 zone
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
# server/location 使用
location /api/ {
limit_req zone=mylimit burst=5 nodelay;
...
}
实测(rate=1r/m burst=1 nodelay)连续连发:第 1、2 次 200(主令牌 + burst),第 3 次即 503;rate 改为 2r/s 且 burst=5 nodelay 时短突发可被桶吸收。参数说明:
rate=2r/s:每秒放行 2 个请求(也可r/m);burst=5:允许的突发事件缓冲量,nodelay表示不排队延迟、直接按令牌瞬时放行;- 默认不加
nodelay时,超出 rate 的请求会被延迟排队而非拒绝。
限并发连接 limit_conn(实测)
limit_conn_zone $binary_remote_addr zone=connzone:10m; # http 块
location /download/ {
limit_conn connzone 2; # 单 IP 最多同时 2 个连接
}
💡 说明:超出并发连接数返回
503。短响应下不易观测(连接快速关闭),生产配合慢响应/大文件下载可明显体现;常与limit_req配合防刷。
反向代理缓存 proxy_cache(实测)
将后端响应缓存到本地磁盘并回源校验,降低后端压力:
# http 块:定义缓存路径与区
proxy_cache_path /var/cache/nginx/upcache levels=1:2 \
keys_zone=upcache:10m max_size=100m inactive=10m use_temp_path=off;
server {
location /proxy/ {
proxy_pass http://backend/;
proxy_cache upcache; # 绑定缓存区
proxy_cache_key $request_uri; # 缓存键=URI
proxy_cache_valid 200 1m; # 200 状态缓存 1 分钟(必须显式声明,否则默认不缓存)
add_header X-Cache-Status $upstream_cache_status; # 返回 MISS/HIT 便于观测
}
}
实测:X-Cache-Status 首次为 MISS,第二次起 HIT;若去掉 proxy_cache_valid 200 1m; 则恒为 MISS(无缓存头声明时不会缓存)。
⚠️ 说明:
proxy_cache_valid是让它真正落到磁盘的关键;命中后即使后端不可用,缓存仍可对外服务(可用proxy_cache_use_stale更进一步)。
服务管理与信号
- 启停/重载:
nginx -s stop|quit|reload|reopen(需以启动用户运行) - 校验/看参:
nginx -t、nginx -V、nginx -T(-T输出最终合并配置,排查 include 是否生效) - 信号:
kill -USR1 <pid>(reopen 日志)、kill -USR2(平滑升级)、kill -QUIT(优雅退出) - 监听检查:
ss -ltnp | grep :80
日志定期清理(logrotate)
Ubuntu/Debian 安装 nginx 自带 /etc/logrotate.d/nginx,默认按天轮转并保留一定份数:
cat /etc/logrotate.d/nginx # 查看默认策略(daily、rotate 等)
# 手动触发一次轮转验证
logrotate -f /etc/logrotate.d/nginx
💡 说明:若日志过大可把
rotate调大、加compress(gzip 压缩历史日志)、dateext(按日期命名)。源码/编译安装无默认 logrotate,可自行写一条类似规则并加入cron。
双机热备 keepalived(多机模板)
⚠️ 本小节为多机部署方案(主备两台 nginx + VIP),需 ≥2 台同网段主机,单机环境未完整复现多机场景,此处作为可落地的配置模板给出。
keepalived 通过 VRRP 在网段内组播竞选,将虚拟 IP(VIP)绑定到 MASTER 网卡,MASTER 故障或 nginx 挂掉时 VIP 漂移给 BACKUP,实现高可用。
# 主备两台均安装(示例基于 CentOS/RHEL;Ubuntu 用 apt-get install keepalived -y)
yum install keepalived -y
systemctl enable keepalived && systemctl start keepalived
/etc/keepalived/keepalived.conf(MASTER 示例,BACKUP 仅把 state 改 BACKUP、priority 调小):
vrrp_script chk_nginx {
script "/usr/local/scripts/check_nginx_pid.sh" # 探活脚本
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER # 主为 MASTER,备为 BACKUP
interface eth0 # 承载 VRRP 的网卡
virtual_router_id 66 # 虚拟路由编号,主备必须一致
priority 100 # 越大越优先,MASTER>BACKUP
advert_int 1
authentication {
auth_type PASS
auth_pass 1111 # 主备密码须一致
}
track_script { chk_nginx }
virtual_ipaddress {
10.10.60.64 # VIP,主备共享,漂移用
}
}
探活脚本 /usr/local/scripts/check_nginx_pid.sh:
#!/bin/bash
if ! pgrep -x nginx >/dev/null; then # nginx 没起就拉起
systemctl start nginx
sleep 2
pgrep -x nginx >/dev/null || systemctl stop keepalived # 拉不起来则让 VIP 漂移
fi
验证:主备均起后 ip addr show eth0 应看到 VIP;手动停止主 nginx/主 keepalived,VIP 会自动切到备机。
rsync + inotify 配置文件实时同步(多机模板)
⚠️ 多机方案(主 A:192.168.0.1 → 备 B:192.168.0.2),同步
/etc/nginx、/etc/letsencrypt、/etc/ssl三目录。单机环境未完整复现多机场景,作为模板给出。
# 主备两机都装 rsync,主上加 inotify-tools
yum install rsync inotify-tools -y # Ubuntu: apt-get install rsync inotify-tools -y
# 备机确认内核支持 inotify(存在下面三个文件即可)
ls /proc/sys/fs/inotify/
# 实测存在: max_queued_events、max_user_instances、max_user_watches
备机(B)/etc/rsyncd.conf(对外提供 nginx_conf 模块,允许主 A 推数据):
uid = root
gid = root
use chroot = yes
read only = no
max connections = 4
secrets file = /etc/rsync.pass # 认证文件
auth users = root
hosts allow = 192.168.0.1 # 只允许主 A
[nginx_conf]
path = /etc/nginx
comment = nginx conf sync from master
[ssl_letsencrypt]
path = /etc/letsencrypt
[ssl_private_ca]
path = /etc/ssl
# 备机:写认证文件并启动 rsync 守护
echo 'root:密码' > /etc/rsync.pass && chmod 600 /etc/rsync.pass
rsync --daemon
# 主机 A:写仅含密码的文件(600 权限,否则报 invalid challenge response)
echo '密码' > /etc/rsync.passwd && chmod 600 /etc/rsync.passwd
主机 A 同步脚本 /usr/local/scripts/inotify_nginx_conf.sh(监控目录变化即增量推到备机):
#!/bin/bash
host=192.168.0.2
src=/etc/nginx
des=nginx_conf # 与备机 rsyncd 模块名一致
/usr/bin/inotifywait -mrq -e modify,delete,create,attrib ${src} \
| while read f; do
rsync -avzP --delete --password-file=/etc/rsync.passwd ${src}/ root@$host::${des}
done
# 主机 A:chmod +x 脚本并后台启动
chmod +x /usr/local/scripts/inotify_*.sh
nohup bash /usr/local/scripts/inotify_nginx_conf.sh > /tmp/inotify.log 2>&1 &
💡 说明:
max_queued_events / max_user_instances / max_user_watches实测为16384 / 128 / 65536,表示事件队列、实例、可监控数量上限;目录/文件较多时需调大。
常见问题
- 状态页 403/404:确认模块已编译(
nginx -V | grep stub_status有--with-http_stub_status_module),且location /status无被父级 location 覆盖。 - JSON 日志无 upstreamtime:未走 upstream 时为
-,属正常。 - $remote_addr 非真实客户端 IP:位于代理后时用
$http_x_forwarded_for取真实 IP。