Nginx 运维与监控方案(变量/日志/stub_status)

整理 Nginx 日常运维用到的内置变量、日志格式(含 JSON 化)、stub_status 状态页监控与 gzip 优化点。stub_status 输出为 Ubuntu 22.04 实测结果

NginxnginxWeb 服务器与反向代理Web 服务器反向代理2026/09/27

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_methodGET/POST/PUT/DELETE
$request_uri含参数的原始 URI($document_uri?$args)
$request_filename请求资源在磁盘的绝对路径(root/alias + URI)
$request_time请求处理耗时(秒,毫秒精度)
$argsURL 全部查询参数
$http_referer来源页面
$http_user_agent客户端 UA
$http_x_forwarded_for反向代理链客户端 IP 记录
$http_<name>任意请求头,小写、横线换下划线
$host请求 Host
$schemehttp/https
$server_addr / $server_name / $server_port服务器地址/名/端口
$server_protocolHTTP/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/x403(拒绝)
无 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。

参考链接