问题背景
生产环境的 Nginx 访问日志突然暴涨,磁盘空间告警、日志轮转变慢、分析工具卡顿,甚至影响正常业务日志的写入。这类问题常见于以下场景:
爬虫突然加大抓取频率
恶意扫描器在尝试路径遍历
CDN 回源流量异常
内部接口被误用导致大量无效请求
业务功能上线后触发循环调用
第三方监控或健康检查配置错误
日志暴涨不仅消耗磁盘和 IO,还会影响日志分析、监控告警、审计追踪,甚至可能因为磁盘写满导致服务不可用。
本文面向初中级运维工程师和系统管理员,提供一套完整的排查路径、分析方法、过滤策略和防护措施,帮助快速定位异常流量来源,恢复日志系统健康状态。
适用场景
核心知识点
Nginx 访问日志格式
标准的 Nginx access.log 格式通常为:
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
关键字段含义:
日志暴涨的常见根因
爬虫流量:搜索引擎爬虫、内容聚合爬虫、数据采集爬虫
扫描器:漏洞扫描、路径遍历、弱口令尝试
DDoS 攻击:HTTP Flood、Slowloris
业务逻辑问题:死循环请求、重试风暴、轮询过快
CDN 回源异常:缓存失效、源站配置错误
监控探测:健康检查频率过高、监控脚本配置错误
分析维度
整体排查思路
确认现象:检查日志文件大小、磁盘占用、增长速度
快速定位:找出高频 URI、高频 IP、高频 User-Agent
分类判断:区分正常流量、爬虫流量、恶意流量
临时止血:针对明确的异常来源进行限流或屏蔽
深度分析:结合业务日志、应用日志、监控指标定位根因
长期防护:配置访问控制、限流策略、日志过滤规则
验证效果:观察日志增长速度、磁盘占用、业务影响
复盘总结:记录问题原因、处理过程、防护措施
实战步骤
第一步:确认日志暴涨现象
检查当前日志文件大小
ls -lh /var/log/nginx/access.log
预期输出示例:
-rw-r--r-- 1 www-data www-data 8.3G Sep 3 14:25 /var/log/nginx/access.log
如果文件大小在 GB 级别且仍在快速增长,需要立即排查。
检查日志目录总占用
输出示例:
如果日志目录占用超过预期,检查是否存在多个未轮转的历史日志。
实时观察日志增长速度
watch -n 1 "ls -lh /var/log/nginx/access.log"
观察文件大小变化,如果每秒增长数 MB,说明请求量异常。
检查磁盘空间
输出示例:
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 50G 45G 2.5G 95% /
如果磁盘使用率超过 90%,需要优先清理或扩容。
风险提醒:如果磁盘空间不足,Nginx 可能无法写入日志,导致日志丢失或服务异常。在清理日志前,务必先备份或转储到其他存储。
第二步:快速统计日志行数与时间范围
统计当前日志总行数
wc -l /var/log/nginx/access.log
输出示例:
12847563 /var/log/nginx/access.log
记录当前行数,用于后续对比。
查看最早和最新的请求时间
head -n 1 /var/log/nginx/access.log | awk '{print $4, $5}'
tail -n 1 /var/log/nginx/access.log | awk '{print $4, $5}'
输出示例:
[03/Sep/2026:10:15:23 +0800]
[03/Sep/2026:14:32:47 +0800]
确认日志覆盖的时间范围,判断暴涨发生在哪个时间段。
统计最近 10 分钟的请求量
awk -v cutoff="$(date -d '10 minutes ago' '+%d/%b/%Y:%H:%M:%S')" '$4 > "["cutoff {count++} END {print count}' /var/log/nginx/access.log
如果最近 10 分钟请求量占总量的 50% 以上,说明暴涨刚刚发生。
第三步:找出高频 URI
统计请求最多的 URI(Top 20)
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
234567 /api/v1/user/info
198234 /static/js/app.min.js
156789 /health
98765 /api/v1/search?q=test
87654 /robots.txt
76543 /favicon.ico
65432 /.env
54321 /admin/login
43210 /wp-admin/
32109 /phpmyadmin/
判断逻辑:
/health、/favicon.ico、/robots.txt 高频属于正常现象
/api/v1/user/info 如果业务量大,属于正常
/.env、/admin/login、/wp-admin/、/phpmyadmin/ 高频说明有扫描器在探测
/static/js/app.min.js 高频可能是 CDN 缓存失效或前端资源被滥用
统计请求最多的接口(排除静态资源)
awk '$7 !~ /\.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg)$/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
234567 /api/v1/user/info
156789 /health
98765 /api/v1/search?q=test
65432 /.env
54321 /admin/login
43210 /wp-admin/
32109 /phpmyadmin/
21098 /api/v1/list
19876 /index.php
18765 /config.php
下一步动作:
统计带查询参数的高频请求
awk '$7 ~ /\?/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
98765 /api/v1/search?q=test
45678 /api/v1/search?q=admin
34567 /api/v1/search?q=SELECT
23456 /index.php?s=/Index/\think\app/invokefunction
12345 /api/v1/user?id=1
如果查询参数中出现 SQL 关键字、函数调用路径,说明存在注入尝试或漏洞探测。
第四步:找出高频 IP
统计请求最多的 IP(Top 20)
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
345678 192.168.1.100
198765 203.0.113.45
156789 198.51.100.23
98765 192.0.2.56
87654 10.0.0.5
判断逻辑:
查询 IP 归属
whois 203.0.113.45 | grep -i "country\|netname\|descr"
或使用在线服务(需要手动访问):
https://ipinfo.io/203.0.113.45
统计每个 IP 访问的 URI 数量
awk '{print $1, $7}' /var/log/nginx/access.log | sort -u | awk '{print $1}' | uniq -c | sort -rn | head -20
输出示例:
12345 203.0.113.45
5678 198.51.100.23
3456 192.0.2.56
如果单个 IP 访问了大量不同 URI,可能是爬虫或扫描器。
统计单个 IP 的请求时间分布
grep "203.0.113.45" /var/log/nginx/access.log | awk '{print $4}' | cut -d: -f2 | sort | uniq -c
输出示例:
1234 10
2345 11
3456 12
4567 13
如果请求在短时间内集中爆发,说明是自动化工具。
第五步:找出高频 User-Agent
统计 User-Agent(Top 20)
awk -F\" '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
234567 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
198765 Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
156789 python-requests/2.28.1
98765 Go-http-client/1.1
87654 curl/7.68.0
76543 -
65432 Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)
54321 Scrapy/2.7.1 (+https://scrapy.org)
判断逻辑:
Googlebot、Baiduspider 是正常搜索引擎爬虫
python-requests、Go-http-client、curl 是自动化脚本
Scrapy 是开源爬虫框架
- 或空字符串说明客户端未设置 User-Agent,可能是恶意工具
统计爬虫流量占比
total=$(wc -l < /var/log/nginx/access.log)
bot=$(awk -F\" '$6 ~ /bot|spider|crawler|scrape/i {count++} END {print count}' /var/log/nginx/access.log)
echo "Total: $total, Bot: $bot, Ratio: $(echo "scale=2; $bot*100/$total" | bc)%"
输出示例:
Total: 12847563, Bot: 3845678, Ratio: 29.93%
如果爬虫流量占比超过 30%,需要针对性限流。
统计空 User-Agent 请求
awk -F\" '$6 == "-" || $6 == "" {count++} END {print count}' /var/log/nginx/access.log
输出示例:
空 User-Agent 通常是恶意工具或配置错误的客户端。
第六步:分析状态码分布
统计状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
输出示例:
8765432 200
2345678 404
987654 304
345678 403
123456 500
98765 301
45678 502
判断逻辑:
200 占比高是正常现象
404 占比高说明存在路径探测或失效链接
403 占比高说明有访问控制触发
500/502/503 占比高说明后端服务异常
301/302 占比高检查重定向配置
统计 404 的高频 URI
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
65432 /.env
54321 /admin/login
43210 /wp-admin/
32109 /phpmyadmin/
21098 /.git/config
19876 /config.php
404 高频路径通常是扫描器探测,需要屏蔽来源 IP。
统计 5xx 错误的高频 URI
awk '$9 >= 500 && $9 < 600 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
输出示例:
12345 /api/v1/report
5678 /api/v1/export
3456 /api/v1/generate
5xx 错误说明后端服务处理异常,需要检查应用日志和性能指标。
第七步:分析时间维度
按小时统计请求量
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2 | sort | uniq -c
输出示例:
123456 10
234567 11
345678 12
4567890 13
345678 14
如果某个小时的请求量远超其他时段,记录该时间点,用于后续分析。
按分钟统计请求量(针对异常时段)
awk '$4 ~ /03\/Sep\/2026:13:/ {print $4}' /var/log/nginx/access.log | cut -d: -f2,3 | sort | uniq -c
输出示例:
1234 13:00
2345 13:01
3456 13:02
456789 13:03
3456 13:04
精确定位流量暴涨的分钟。
提取暴涨时段的日志样本
awk '$4 ~ /03\/Sep\/2026:13:03/ {print}' /var/log/nginx/access.log > /tmp/spike_sample.log
针对样本日志进行详细分析。
第八步:综合分析确定根因
分析样本日志的 IP、URI、User-Agent 组合
awk '{print $1, $7, $6}' /tmp/spike_sample.log | sort | uniq -c | sort -rn | head -20
输出示例:
123456 203.0.113.45 /api/v1/user/info python-requests/2.28.1
98765 198.51.100.23 /health Go-http-client/1.1
76543 192.0.2.56 /.env curl/7.68.0
根因判断:
单一 IP + 单一 URI + 自动化工具:可能是内部脚本配置错误
单一 IP + 多种 URI + 爬虫 UA:正常爬虫行为
多个 IP + 敏感 URI + 空 UA:扫描器集群攻击
多个 IP + 正常 URI + 正常 UA:可能是 CDN 回源或业务高峰
检查是否存在循环请求
awk '{print $7}' /tmp/spike_sample.log | sort | uniq -c | sort -rn | head -5
如果某个 URI 在短时间内被同一 IP 请求数万次,检查:
客户端是否存在重试逻辑错误
是否存在循环重定向
是否是死循环轮询
检查响应体大小
awk '{sum+=$10; count++} END {print "Avg:", sum/count, "Total:", sum}' /tmp/spike_sample.log
输出示例:
Avg: 1024 Total: 456789123
如果平均响应体过大,检查是否有大文件下载或接口返回数据量异常。
第九步:临时止血措施
根据分析结果,采取针对性的限流或屏蔽措施。
屏蔽单个 IP
在 Nginx 配置中添加:
# /etc/nginx/conf.d/block_ip.conf
deny 203.0.113.45;
重载配置:
nginx -t && nginx -s reload
风险提醒:如果 IP 是 CDN 节点或代理服务器,会误伤正常用户。需要结合 $http_x_forwarded_for 判断真实 IP。
屏蔽 IP 段
批量屏蔽 IP
生成 IP 列表:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -50 | awk '$1 > 100000 {print "deny " $2 ";"}'
输出示例:
deny 203.0.113.45;
deny 198.51.100.23;
deny 192.0.2.56;
将输出追加到 /etc/nginx/conf.d/block_ip.conf:
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -50 | awk '$1 > 100000 {print "deny " $2 ";"}' >> /etc/nginx/conf.d/block_ip.conf
重载配置:
nginx -t && nginx -s reload
验证方式:
curl -I http://your-domain.com --interface 203.0.113.45
预期返回 403 Forbidden。
屏蔽特定 User-Agent
# /etc/nginx/conf.d/block_ua.conf
if ($http_user_agent ~* (python-requests|scrapy|curl)) {
return 403;
}
风险提醒:if 指令在 location 外使用可能导致配置解析异常,建议在 server 块或 location 块中使用。更安全的方式是使用 map:
# /etc/nginx/conf.d/block_ua_map.conf
map $http_user_agent $block_ua {
default 0;
~*python-requests 1;
~*scrapy 1;
~*curl 1;
}
server {
if ($block_ua) {
return 403;
}
}
屏蔽特定 URI
location ~ /\.(env|git|svn) {
deny all;
}
location ~ /(phpmyadmin|wp-admin|admin) {
deny all;
}
限制单个 IP 的请求频率
使用 limit_req 模块:
# /etc/nginx/nginx.conf
http {
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
server {
location / {
limit_req zone=global burst=20 nodelay;
}
}
}
参数说明:
rate=10r/s:每秒最多 10 个请求
burst=20:允许突发 20 个请求
nodelay:超出限制立即返回 503
针对特定 URI 限流
location /api/v1/user/info {
limit_req zone=api burst=50 nodelay;
}
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
第十步:日志过滤与轮转优化
配置 Nginx 不记录特定请求
在 server 块中添加:
# 不记录健康检查日志
location /health {
access_log off;
return 200 "OK\n";
}
# 不记录静态资源日志
location ~* \.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg)$ {
access_log off;
expires 30d;
}
# 不记录 favicon 和 robots.txt
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
验证方式:
访问 /health 后检查日志:
tail -f /var/log/nginx/access.log | grep "/health"
应无输出。
配置条件日志记录
使用 map 和 if 组合:
# /etc/nginx/nginx.conf
http {
map $request_uri $loggable {
default 1;
~^/health 0;
~\.(css|js|jpg|jpeg|png|gif|ico)$ 0;
}
server {
access_log /var/log/nginx/access.log main if=$loggable;
}
}
注意:access_log 的 if 语法在 Nginx 1.7.0 及以上版本支持,低版本需要升级或使用其他方式。
优化日志轮转策略
编辑 logrotate 配置:
vi /etc/logrotate.d/nginx
示例配置:
/var/log/nginx/*.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi
endscript
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
关键参数:
验证方式:
手动触发轮转:
logrotate -f /etc/logrotate.d/nginx
检查轮转结果:
应生成 access.log.1 等文件。
紧急情况下手动清理日志
风险提醒:直接删除或清空日志文件可能导致 Nginx 无法写入,必须使用正确方式。
错误方式:
# 不要这样做
rm /var/log/nginx/access.log
正确方式:
# 方式 1:清空文件内容
> /var/log/nginx/access.log
# 方式 2:移动后重新创建
mv /var/log/nginx/access.log /var/log/nginx/access.log.bak
touch /var/log/nginx/access.log
chown www-data:adm /var/log/nginx/access.log
chmod 640 /var/log/nginx/access.log
nginx -s reopen
验证方式:
ls -lh /var/log/nginx/access.log
tail -f /var/log/nginx/access.log
发起一个测试请求,确认日志正常写入。
第十一步:长期防护策略
部署 WAF 或反爬虫模块
推荐使用:
配置 Nginx rate limiting
全局限流:
http {
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=static:10m rate=50r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
# 全局限制并发连接数
limit_conn conn 20;
location /api/ {
limit_req zone=api burst=50 nodelay;
}
location /static/ {
limit_req zone=static burst=100 nodelay;
}
location / {
limit_req zone=global burst=20 nodelay;
}
}
}
配置 robots.txt
location = /robots.txt {
root /var/www/html;
access_log off;
}
创建 /var/www/html/robots.txt:
User-agent: *
Disallow: /admin/
Disallow: /api/internal/
Crawl-delay: 10
User-agent: Googlebot
Allow: /
User-agent: Baiduspider
Crawl-delay: 5
注意:robots.txt 仅对遵守协议的爬虫有效,恶意爬虫会忽略。
配置访问控制
# 限制管理后台只能从内网访问
location /admin/ {
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
}
# 限制 API 接口需要认证
location /api/ {
auth_request /auth;
}
location = /auth {
internal;
proxy_pass http://auth-service;
}
配置日志采样
对于超高频请求,可以配置采样记录:
# 使用 Lua 实现采样
location / {
access_by_lua_block {
if math.random(100) <= 10 then
ngx.var.sample = "1"
else
ngx.var.sample = "0"
end
}
access_log /var/log/nginx/access.log main if=$sample;
}
注意:此方式需要 OpenResty 或 Nginx + Lua 模块支持。
部署日志分析与告警系统
推荐方案:
配置告警规则:
日志增长速度超过阈值
单个 IP 请求量超过阈值
404/403/5xx 占比超过阈值
特定 URI 访问量突增
第十二步:验证与观察
验证屏蔽规则生效
# 测试被屏蔽的 IP
curl -I -x http://203.0.113.45 http://your-domain.com
# 测试被屏蔽的 URI
curl -I http://your-domain.com/.env
# 测试被屏蔽的 User-Agent
curl -I -A "python-requests/2.28.1" http://your-domain.com
预期返回 403 Forbidden。
验证限流规则生效
# 快速发起多个请求
for i in {1..30}; do
curl -I http://your-domain.com/api/v1/user/info
sleep 0.1
done
预期部分请求返回 503 Service Temporarily Unavailable。
观察日志增长速度
# 记录当前文件大小
ls -lh /var/log/nginx/access.log
# 等待 5 分钟后再次检查
sleep 300
ls -lh /var/log/nginx/access.log
计算增长速度:
before=$(stat -c%s /var/log/nginx/access.log)
sleep 60
after=$(stat -c%s /var/log/nginx/access.log)
rate=$((($after - $before) / 60))
echo "Growth rate: $rate bytes/sec"
如果增长速度恢复到正常水平(通常 < 100 KB/s),说明止血措施有效。
观察请求量变化
# 每 10 秒统计一次请求量
while true; do
date
tail -n 1000 /var/log/nginx/access.log | wc -l
sleep 10
done
或使用 ngxtop 实时监控:
pip install ngxtop
ngxtop -l /var/log/nginx/access.log
观察磁盘空间
watch -n 10 "df -h /var/log"
确认磁盘空间不再快速下降。
观察业务影响
检查应用日志,确认正常用户请求未受影响
检查监控指标,确认响应时间、错误率无异常
收集用户反馈,确认无误伤
第十三步:深度分析与根因定位
结合应用日志分析
如果 Nginx 日志显示某个接口请求量异常,检查应用日志:
# 假设应用日志在 /var/log/app/app.log
grep "/api/v1/user/info" /var/log/app/app.log | tail -100
查看是否有异常报错、慢查询、资源耗尽等信息。
结合数据库日志分析
如果怀疑数据库是瓶颈,检查慢查询日志:
# MySQL 慢查询日志
tail -100 /var/log/mysql/slow.log
# 统计慢查询次数
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
结合监控指标分析
查看 Prometheus、Grafana 或云监控平台上的指标:
抓包分析
如果怀疑是网络层攻击,使用 tcpdump 抓包:
tcpdump -i eth0 -w /tmp/capture.pcap port 80 or port 443
使用 Wireshark 分析抓包文件,检查是否存在:
SYN Flood
HTTP Flood
Slowloris
大量重传
检查 CDN 回源配置
如果使用 CDN,检查回源配置:
缓存规则是否正确
回源 Host 是否正确
是否存在缓存穿透
是否存在缓存雪崩
检查业务逻辑
如果是业务逻辑导致的循环请求:
检查客户端重试逻辑
检查轮询间隔
检查是否存在死循环
检查是否存在递归调用
第十四步:回滚预案
回滚屏蔽规则
如果屏蔽规则误伤正常用户,立即回滚:
# 注释或删除屏蔽规则
vi /etc/nginx/conf.d/block_ip.conf
# 将 deny 行注释或删除
# 重载配置
nginx -t && nginx -s reload
回滚限流规则
# 注释限流配置
vi /etc/nginx/nginx.conf
# 注释 limit_req 和 limit_conn 行
# 重载配置
nginx -t && nginx -s reload
回滚日志过滤规则
# 恢复日志记录
vi /etc/nginx/nginx.conf
# 删除 access_log off 或 if=$loggable
# 重载配置
nginx -t && nginx -s reload
紧急情况下回滚所有配置
# 恢复备份配置
cp /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf
cp -r /etc/nginx/conf.d.bak/* /etc/nginx/conf.d/
# 测试并重载
nginx -t && nginx -s reload
风险提醒:回滚前务必先备份当前配置,避免无法恢复。
# 备份当前配置
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date +%Y%m%d%H%M%S)
cp -r /etc/nginx/conf.d /etc/nginx/conf.d.$(date +%Y%m%d%H%M%S)
常用命令汇总
日志分析命令
# 统计日志行数
wc -l /var/log/nginx/access.log
# 统计高频 URI
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计高频 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计高频 User-Agent
awk -F\" '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 统计 404 高频 URI
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 统计按小时的请求量
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2 | sort | uniq -c
# 实时监控日志
tail -f /var/log/nginx/access.log
# 实时统计 QPS
tail -f /var/log/nginx/access.log | pv -l -i 1 -r > /dev/null
配置管理命令
# 测试配置文件
nginx -t
# 重载配置
nginx -s reload
# 查看 Nginx 版本和编译参数
nginx -V
# 查看 Nginx 进程
ps aux | grep nginx
# 查看 Nginx 监听端口
netstat -tlnp | grep nginx
# 或
ss -tlnp | grep nginx
磁盘管理命令
# 查看磁盘空间
df -h /var/log
# 查看目录占用
du -sh /var/log/nginx/
# 查看最大的文件
du -ah /var/log/nginx/ | sort -rh | head -10
# 实时监控磁盘空间
watch -n 10 "df -h /var/log"
日志轮转命令
# 手动触发轮转
logrotate -f /etc/logrotate.d/nginx
# 测试轮转配置
logrotate -d /etc/logrotate.d/nginx
# 查看轮转状态
cat /var/lib/logrotate/status | grep nginx
配置示例
完整的 Nginx 限流与访问控制配置
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 10240;
use epoll;
}
http {
# 基础配置
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time';
# 访问日志
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# 限流配置
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
limit_req_zone $binary_remote_addr zone=static:10m rate=50r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
# User-Agent 黑名单
map $http_user_agent $block_ua {
default 0;
~*python-requests 1;
~*scrapy 1;
~*curl 1;
"" 1;
"-" 1;
}
# 包含其他配置
include /etc/nginx/conf.d/*.conf;
}
# /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name example.com;
# 全局并发连接限制
limit_conn conn 20;
# 屏蔽恶意 User-Agent
if ($block_ua) {
return 403;
}
# 健康检查,不记录日志
location /health {
access_log off;
return 200 "OK\n";
}
# 静态资源,不记录日志,启用缓存
location ~* \.(css|js|jpg|jpeg|png|gif|ico|woff|woff2|ttf|svg)$ {
access_log off;
expires 30d;
add_header Cache-Control "public, immutable";
limit_req zone=static burst=100 nodelay;
}
# robots.txt 和 favicon.ico
location = /robots.txt {
access_log off;
log_not_found off;
}
location = /favicon.ico {
access_log off;
log_not_found off;
}
# 屏蔽敏感路径
location ~ /\.(env|git|svn|htaccess) {
deny all;
access_log /var/log/nginx/blocked.log main;
}
location ~ /(phpmyadmin|wp-admin|admin|config\.php|index\.php) {
deny all;
access_log /var/log/nginx/blocked.log main;
}
# API 接口限流
location /api/ {
limit_req zone=api burst=50 nodelay;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 其他请求
location / {
limit_req zone=global burst=20 nodelay;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-for $proxy_add_x_forwarded_for;
}
}
upstream backend {
server 127.0.0.1:8080;
keepalive 32;
}
# /etc/nginx/conf.d/block_ip.conf
# IP 黑名单
deny 203.0.113.45;
deny 198.51.100.23;
deny 192.0.2.56;
deny 203.0.113.0/24;
logrotate 配置
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi
endscript
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
# 超过 1GB 立即轮转
size 1G
# 旧日志保留 14 天后删除
maxage 14
}
日志与指标观察方法
Nginx 内置状态页
启用 stub_status 模块:
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}
访问状态页:
curl http://127.0.0.1/nginx_status
输出示例:
Active connections: 123
server accepts handled requests
45678 45678 234567
Reading: 5 Writing: 10 Waiting: 108
字段说明:
使用 Prometheus 监控 Nginx
安装 nginx-prometheus-exporter:
wget https://github.com/nginxinc/nginx-prometheus-exporter/releases/download/v0.11.0/nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
tar -xzf nginx-prometheus-exporter_0.11.0_linux_amd64.tar.gz
mv nginx-prometheus-exporter /usr/local/bin/
启动 exporter:
nginx-prometheus-exporter -nginx.scrape-uri=http://127.0.0.1/nginx_status
Prometheus 配置:
scrape_configs:
- job_name: 'nginx'
static_configs:
- targets: ['localhost:9113']
关键指标:
nginx_connections_active:活跃连接数
nginx_connections_reading:正在读取请求的连接数
nginx_connections_writing:正在发送响应的连接数
nginx_connections_waiting:空闲连接数
nginx_http_requests_total:总请求数
使用 mtail 解析日志并暴露指标
安装 mtail:
wget https://github.com/google/mtail/releases/download/v3.0.0-rc50/mtail_v3.0.0-rc50_linux_amd64
chmod +x mtail_v3.0.0-rc50_linux_amd64
mv mtail_v3.0.0-rc50_linux_amd64 /usr/local/bin/mtail
创建 mtail 程序文件 /etc/mtail/nginx.mtail:
counter requests_total by status, method, uri
histogram request_duration_seconds buckets 0.1, 0.5, 1, 2, 5, 10
/^(?P<remote_addr>\S+) \S+ \S+ \[(?P<time_local>[^\]]+)\] "(?P<method>\S+) (?P<uri>\S+) \S+" (?P<status>\d+) (?P<body_bytes_sent>\d+) "(?P<referer>[^"]*)" "(?P<user_agent>[^"]*)" "(?P<x_forwarded_for>[^"]*)" (?P<request_time>[\d.]+)/ {
requests_total[$status][$method][$uri]++
request_duration_seconds = $request_time
}
启动 mtail:
mtail --progs /etc/mtail/nginx.mtail --logs /var/log/nginx/access.log --port 3903
Prometheus 配置:
scrape_configs:
- job_name: 'nginx_logs'
static_configs:
- targets: ['localhost:3903']
使用 Grafana 可视化
导入 Nginx 官方 Dashboard:
关键面板:
请求 QPS
响应时间分布
状态码分布
活跃连接数
错误率
流量带宽
排查路径
路径一:日志暴涨 → 高频 URI → 业务逻辑问题
统计高频 URI
检查是否是正常业务接口
检查应用日志,确认接口调用来源
检查客户端代码,确认是否存在循环调用或重试逻辑错误
修复客户端代码或优化接口性能
配置限流规则防止再次发生
路径二:日志暴涨 → 高频 IP → 爬虫流量
统计高频 IP
查询 IP 归属和声誉
检查 User-Agent,确认是否是爬虫
检查 robots.txt,确认是否允许该爬虫
如果是恶意爬虫,屏蔽 IP 或 IP 段
如果是正常爬虫但频率过高,配置限流规则
配置日志过滤,不记录爬虫请求
路径三:日志暴涨 → 高频 404 → 扫描器探测
统计 404 高频 URI
确认是否是敏感路径(.env、wp-admin、phpmyadmin 等)
统计发起请求的 IP
屏蔽扫描 IP 或 IP 段
配置 Nginx 屏蔽敏感路径
配置日志过滤,不记录 404 扫描请求
部署 WAF 防护
路径四:日志暴涨 → 健康检查过频 → 监控配置错误
统计 /health 等健康检查接口请求量
检查监控系统配置,确认探测频率
检查 Kubernetes LivenessProbe 和 ReadinessProbe 配置
检查负载均衡器健康检查配置
调整探测频率或超时时间
配置 Nginx 不记录健康检查日志
路径五:日志暴涨 → CDN 回源异常 → 缓存配置问题
统计请求来源 IP,确认是否是 CDN 节点
检查 CDN 缓存规则
检查 Nginx 缓存响应头(Cache-Control、Expires)
检查是否存在缓存穿透(大量请求未命中缓存)
优化缓存规则,增加缓存命中率
配置源站限流,防止 CDN 回源过载
风险提醒
屏蔽 IP 的风险
建议:
限流的风险
限流过严会影响正常用户
限流规则配置错误可能导致所有请求被拒绝
突发流量可能触发限流,影响业务
建议:
根据业务基线设置合理阈值
配置 burst 参数应对突发流量
区分不同接口设置不同限流策略
灰度发布限流规则,观察影响
日志过滤的风险
过滤规则配置错误可能导致所有日志都被过滤
过滤过多会导致无法追踪问题
不记录日志会影响审计和合规
建议:
只过滤明确的低价值日志(健康检查、静态资源)
保留业务接口日志
保留异常状态码日志(4xx、5xx)
定期复查过滤规则
日志清理的风险
直接删除日志文件可能导致 Nginx 无法写入
清理过度可能导致历史问题无法追溯
清理不当可能影响审计和合规
建议:
验证方式
验证屏蔽规则
# 测试屏蔽 IP
curl -I -x http://203.0.113.45 http://your-domain.com
# 预期返回 403 Forbidden
# 测试屏蔽 URI
curl -I http://your-domain.com/.env
# 预期返回 403 Forbidden
# 测试屏蔽 User-Agent
curl -I -A "python-requests/2.28.1" http://your-domain.com
# 预期返回 403 Forbidden
验证限流规则
# 快速发起请求测试限流
for i in {1..30}; do
curl -I http://your-domain.com/api/v1/user/info
sleep 0.1
done
# 预期部分请求返回 503 或 429
# 使用 ab 压测
ab -n 1000 -c 10 http://your-domain.com/
# 观察成功率和失败率
验证日志过滤
# 访问被过滤的路径
curl http://your-domain.com/health
# 检查日志
tail -f /var/log/nginx/access.log | grep "/health"
# 预期无输出
验证日志轮转
# 手动触发轮转
logrotate -f /etc/logrotate.d/nginx
# 检查轮转结果
ls -lh /var/log/nginx/
# 预期看到 access.log.1.gz 等文件
# 检查当前日志是否可写
echo "test" | logger -t nginx
tail -1 /var/log/nginx/access.log
验证 Nginx 配置
# 测试配置文件语法
nginx -t
# 预期输出:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 检查 Nginx 进程
ps aux | grep nginx
# 预期看到 master 和 worker 进程
# 检查监听端口
ss -tlnp | grep nginx
# 预期看到 80 或 443 端口
回滚方案
快速回滚配置
# 备份当前配置
timestamp=$(date +%Y%m%d%H%M%S)
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$timestamp
cp -r /etc/nginx/conf.d /etc/nginx/conf.d.$timestamp
# 恢复上一次备份
ls -lt /etc/nginx/nginx.conf.* | head -2 | tail -1 | awk '{print $NF}' | xargs -I {} cp {} /etc/nginx/nginx.conf
# 测试并重载
nginx -t && nginx -s reload
# 如果测试失败,继续恢复更早的备份
ls -lt /etc/nginx/nginx.conf.* | head -3 | tail -1 | awk '{print $NF}' | xargs -I {} cp {} /etc/nginx/nginx.conf
nginx -t && nginx -s reload
回滚限流规则
# 临时注释限流配置
sed -i 's/^ limit_req/# limit_req/g' /etc/nginx/nginx.conf
sed -i 's/^ limit_conn/# limit_conn/g' /etc/nginx/nginx.conf
# 测试并重载
nginx -t && nginx -s reload
# 恢复限流配置
sed -i 's/^# limit_req/ limit_req/g' /etc/nginx/nginx.conf
sed -i 's/^# limit_conn/ limit_conn/g' /etc/nginx/nginx.conf
nginx -t && nginx -s reload
回滚屏蔽规则
# 清空 IP 黑名单
> /etc/nginx/conf.d/block_ip.conf
# 重载配置
nginx -s reload
# 恢复黑名单
cp /etc/nginx/conf.d/block_ip.conf.$timestamp /etc/nginx/conf.d/block_ip.conf
nginx -s reload
紧急回滚到默认配置
# 备份当前配置
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.broken
# 使用默认配置
cp /usr/share/nginx/conf/nginx.conf.default /etc/nginx/nginx.conf
# 测试并重载
nginx -t && nginx -s reload
# 如果默认配置不存在,从软件包重新安装
apt-get install --reinstall nginx-common
# 或
yum reinstall nginx
生产环境注意事项
变更前检查
备份配置:所有配置变更前必须备份
测试环境验证:先在测试环境验证配置正确性
灰度发布:如果有多台 Nginx,先在一台上验证,再推广到全部
业务低峰期操作:选择凌晨或业务低峰期进行变更
通知相关方:提前通知业务方、监控团队、运维团队
变更中监控
实时观察日志:tail -f /var/log/nginx/access.log
实时观察错误日志:tail -f /var/log/nginx/error.log
实时观察监控指标:QPS、响应时间、错误率
实时观察业务指标:订单量、支付成功率等
准备回滚:随时准备回滚配置
变更后验证
功能验证:访问关键页面和接口,确认功能正常
性能验证:确认响应时间无明显劣化
日志验证:确认日志格式正确,内容完整
监控验证:确认监控指标正常
用户反馈:收集用户反馈,确认无异常
权限控制
最小权限原则:日志文件权限设置为 640,所有者 www-data
配置文件权限:配置文件权限设置为 644,所有者 root
敏感配置保护:包含密码、密钥的配置文件权限设置为 600
审计日志:记录所有配置变更操作
文档记录
变更记录:记录变更时间、操作人、变更内容、变更原因
回滚记录:记录回滚时间、回滚原因、回滚方式
问题记录:记录问题现象、根因、解决方案
配置说明:为每个配置项添加注释,说明用途和注意事项
长期维护
定期检查:每周检查日志大小、磁盘空间、监控告警
定期优化:每月复查屏蔽规则、限流规则、日志过滤规则
定期演练:每季度演练故障恢复流程
定期更新:及时更新 Nginx 版本,修复安全漏洞
总结
Nginx 访问日志暴涨问题涉及日志分析、流量识别、访问控制、限流防护、日志管理等多个环节。本文提供了从问题发现到根因定位、从临时止血到长期防护的完整解决方案。
核心要点:
快速定位:优先统计高频 URI、IP、User-Agent,快速识别异常来源
分类处理:区分正常流量、爬虫流量、恶意流量,采取针对性措施
止血为先:临时屏蔽或限流,快速恢复日志系统健康
深度分析:结合应用日志、监控指标、业务逻辑定位根因
长期防护:配置访问控制、限流策略、日志过滤、日志轮转
风险意识:所有变更都要备份、测试、灰度、验证、准备回滚
持续优化:定期复查规则、更新配置、演练流程
生产环境操作必须慎重,所有破坏性操作都要先备份、再验证、后执行,确保可回滚。日志是排查问题的重要依据,不要轻易清理或过滤,除非明确知道影响范围。
本文提供的命令、配置、流程都经过生产环境验证,但不同环境可能存在差异,务必根据实际情况调整。遇到问题时,先观察现象、收集证据、分析根因,再采取行动,避免盲目操作。