Nginx 访问日志暴涨排查手册 — 14 步定位异常 URI 与爬虫流量
来源:微信公众号「马哥Linux运维」(2026-09-03)。场景:生产环境 Nginx 访问日志突然暴涨——磁盘空间告警、日志轮转变慢、分析工具卡顿,甚至影响正常业务日志写入。本手册给出从确认现象 → 五维分析 → 临时止血 → 长期防护 → 验证回滚的完整 14 步流程。与按知识域组织的 nginx-log-analysis-troubleshooting-guide(字段/超时/状态码手册)互补:那一页回答"这个字段/状态码什么意思",本页回答"日志暴涨了,现在按什么顺序做什么"。
零、暴涨的六类根因与六个分析维度
常见根因:
| 根因 | 典型特征 |
|---|---|
| 爬虫流量 | 搜索引擎/内容聚合/数据采集爬虫加大抓取频率 |
| 扫描器 | 漏洞扫描、路径遍历(/.env、/wp-admin)、弱口令尝试 |
| DDoS 攻击 | HTTP Flood、Slowloris |
| 业务逻辑问题 | 死循环请求、重试风暴、轮询过快 |
| CDN 回源异常 | 缓存失效、源站配置错误 |
| 监控探测 | 健康检查频率过高、监控脚本配置错误 |
六个分析维度:URI(哪些路径高频)、IP(哪些来源大量请求)、User-Agent(哪些客户端异常)、状态码(404/403/5xx 占比)、时间(请求量在时间轴的分布)、响应体大小(大文件下载或接口返回过大)。
整体思路八阶段:确认现象 → 快速定位(高频 URI/IP/UA)→ 分类判断(正常/爬虫/恶意)→ 临时止血(限流或屏蔽)→ 深度分析(结合业务/应用/监控定位根因)→ 长期防护(访问控制/限流/日志过滤)→ 验证效果(增长速度/磁盘/业务影响)→ 复盘总结。
一、确认现象与建立基线(步骤 1-2)
ls -lh /var/log/nginx/access.log # GB 级且仍在快速增长 → 立即排查
du -sh /var/log/nginx/ # 目录总占用,检查未轮转历史日志
watch -n 1 "ls -lh /var/log/nginx/access.log" # 每秒增长数 MB = 请求量异常
df -h /var/log # 磁盘使用率超 90% 优先清理或扩容
wc -l /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}' # 最新请求时间
# 最近 10 分钟请求量(占总量的 50% 以上 → 暴涨刚刚发生)
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
⚠️ 风险提醒:磁盘写满时 Nginx 无法写日志,会导致日志丢失或服务异常(参考 troubleshooting-guide 案例 5:磁盘满导致全站 502)。清理前先备份或转储到其他存储。
二、五维分析定位异常(步骤 3-7)
步骤 3:高频 URI
# Top 20 URI
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 只看接口(排除静态资源)
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
# 带查询参数的高频请求(参数出现 SQL 关键字/函数调用路径 = 注入或漏洞探测)
awk '$7 ~ /\?/ {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
判断逻辑:/health、/favicon.ico、/robots.txt 高频属正常;业务接口占比过高查客户端轮询;/.env、/admin/login、/wp-admin/、/phpmyadmin/、/.git/config 高频 = 扫描器探测,立即屏蔽来源 IP;静态资源高频可能是 CDN 缓存失效;/health 异常高频检查监控配置。
步骤 4:高频 IP
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 每个 IP 访问了多少个不同 URI(单 IP 大量不同 URI = 爬虫/扫描器)
awk '{print $1, $7}' /var/log/nginx/access.log | sort -u | awk '{print $1}' | uniq -c | sort -rn | head -20
# 单个 IP 的请求按小时分布(短时间集中爆发 = 自动化工具)
grep "203.0.113.45" /var/log/nginx/access.log | awk '{print $4}' | cut -d: -f2 | sort | uniq -c
whois 203.0.113.45 | grep -i "country\|netname\|descr" # 或 https://ipinfo.io/<IP>
判断逻辑:单个 IP 请求量占总量 10% 以上重点关注;内网地址(10.x/172.16.x/192.168.x)查内部服务或监控系统;公网地址查归属和声誉。
步骤 5:高频 User-Agent
awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 爬虫流量占比(超过 30% 需针对性限流)
total=$(wc -l < /var/log/nginx/access.log)
bot=$(awk -F'"' 'tolower($6) ~ /bot|spider|crawler|scrape/ {count++} END {print count}' /var/log/nginx/access.log)
echo "Total: $total, Bot: $bot, Ratio: $(echo "scale=2; $bot*100/$total" | bc)%"
# 空 User-Agent 请求(通常是恶意工具或配置错误的客户端)
awk -F'"' '$6 == "-" || $6 == "" {count++} END {print count}' /var/log/nginx/access.log
判断逻辑:Googlebot/Baiduspider 是正常搜索引擎爬虫;python-requests/Go-http-client/curl 是自动化脚本;Scrapy 是开源爬虫框架;空 UA 高度可疑。
步骤 6:状态码分布
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# 404 高频 URI(敏感路径 = 扫描器探测,屏蔽来源 IP)
awk '$9 == 404 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# 5xx 高频 URI(后端服务异常,查应用日志与性能指标)
awk '$9 >= 500 && $9 <= 599 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
404 占比高 = 路径探测或失效链接;403 高 = 访问控制触发;5xx 高 = 后端异常;301/302 高 = 检查重定向配置。状态码逐项根因见 nginx-log-analysis-troubleshooting-guide 第三/四节。
步骤 7:时间维度
# 按小时统计请求量(某小时远超其他时段 → 记录该时间点)
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2 | sort | uniq -c
# 异常时段按分钟统计(精确定位暴涨分钟)
awk '$4 ~ /03\/Sep\/2026:13:/ {print $4}' /var/log/nginx/access.log | cut -d: -f2,3 | sort | uniq -c
# 提取暴涨时段样本,后续针对样本分析
awk '$4 ~ /03\/Sep\/2026:13:03/ {print}' /var/log/nginx/access.log > /tmp/spike_sample.log
三、综合定根因(步骤 8)
IP + URI + UA 组合矩阵(对样本日志):
awk -F'"' '{split($1,a," "); print a[1], a[7], $6}' /tmp/spike_sample.log | sort | uniq -c | sort -rn | head -20
| 组合特征 | 根因判断 |
|---|---|
| 单一 IP + 单一 URI + 自动化工具 UA | 内部脚本配置错误 |
| 单一 IP + 多种 URI + 爬虫 UA | 正常爬虫行为 |
| 多个 IP + 敏感 URI + 空 UA | 扫描器集群攻击 |
| 多个 IP + 正常 URI + 正常 UA | CDN 回源或业务高峰 |
补充检查:某 URI 被同一 IP 短时间请求数万次 → 查客户端重试逻辑错误/循环重定向/死循环轮询;平均响应体过大 → 查大文件下载或接口返回数据量异常(awk '{sum+=$10; count++} END {print "Avg:", sum/count}' /tmp/spike_sample.log)。
四、临时止血(步骤 9)
# 批量生成 Top 50 中请求数 >10 万的 deny 规则
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
# 屏蔽单个 IP / IP 段(/etc/nginx/conf.d/block_ip.conf)
deny 203.0.113.45;
deny 203.0.113.0/24;
# 屏蔽敏感 URI
location ~ /\.(env|git|svn) { deny all; }
location ~ /(phpmyadmin|wp-admin|admin) { deny all; }
# 屏蔽恶意 UA —— 比 if 正则更安全的是 map(if 在 location 外使用可能导致解析异常)
map $http_user_agent $block_ua {
default 0;
~*python-requests 1;
~*scrapy 1;
~*curl 1;
}
server { if ($block_ua) { return 403; } }
# 全局限流:rate=每秒上限,burst=允许突发,nodelay=超限立即拒绝(默认 503)
limit_req_zone $binary_remote_addr zone=global:10m rate=10r/s;
server { location / { limit_req zone=global burst=20 nodelay; } }
# 针对特定 URI 单独限流(高频接口)
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;
server { location /api/v1/user/info { limit_req zone=api burst=50 nodelay; } }
⚠️ 屏蔽前必查:目标 IP 是否 CDN 节点/代理服务器/公司出口/运营商 NAT——误伤面是整片用户;结合 $http_x_forwarded_for 判断真实 IP(真实 IP 恢复配置见 nginx-security-config-guide)。优先限流、其次屏蔽;屏蔽要设有效期并记录原因。
五、日志过滤与轮转优化(步骤 10)
# 不记录低价值请求(暴涨的直接止血)
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; }
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
# 条件日志记录:map 控制哪些 URI 记日志(if= 语法需 Nginx 1.7.0+)
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; }
logrotate 优化(/etc/logrotate.d/nginx):daily + rotate 7~14 + compress + delaycompress(延迟一天压缩,避免压缩当天日志)+ size 1G(超 1GB 立即轮转)+ maxage 14(旧日志保留 14 天);postrotate 用 kill -USR1 让 Nginx 重新打开日志文件(不能 nginx -s reload,会断连接)。验证:logrotate -f /etc/logrotate.d/nginx 后 ls -lh /var/log/nginx/ 应出现 access.log.1。
紧急手动清理——禁止 rm:
# ❌ 不要 rm:fd 仍被持有,空间不释放且 Nginx 写入失效(幽灵文件,见 [[linux-deleted-file-disk-space-reclaim]])
# ✅ 方式 1:清空内容(保留 fd)
> /var/log/nginx/access.log
# ✅ 方式 2:移动后重建 + 让 Nginx 重开文件
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
# 验证:发起测试请求后 tail -f 能看到新日志
六、长期防护(步骤 11)
- WAF/反爬:ModSecurity(OWASP CRS)、Naxsi、lua-resty-waf(OpenResty)、云厂商 WAF;接入姿势(先 DetectionOnly 后拦截、只对高风险路径开启)见 nginx-security-config-guide。
- 分级限流:全局 10r/s、API 100r/s、静态 50r/s 分 zone 定义 +
limit_conn conn 20限并发;按接口差异化阈值。 - robots.txt:
Crawl-delay: 10控制抓取间隔、Disallow 屏蔽 /admin/ 与内部 API、对 Googlebot/Baiduspider 单独放行——仅对遵守协议的爬虫有效,恶意爬虫会忽略。 - 访问控制:管理后台仅内网
allow 192.168.0.0/16; deny all;;API 用auth_request子请求认证。 - 日志采样(超高频接口):OpenResty
access_by_lua_block { if math.random(100) <= 10 then ngx.var.sample = "1" else ngx.var.sample = "0" end }+access_log ... if=$sample(需 Lua 模块)。 - 日志平台与告警:ELK / EFK / Loki+Grafana / Graylog;四类告警维度——日志增长速度超阈值、单 IP 请求量超阈值、404/403/5xx 占比超阈值、特定 URI 访问量突增。
七、验证与观察(步骤 12)
# 屏蔽规则:从被屏蔽 IP(或等效代理路径)发起请求,预期 403;无法直接测试则观察日志中该 IP 命中后的状态码
curl -I http://your-domain.com/.env # 屏蔽 URI → 403
curl -I -A "python-requests/2.28.1" http://your-domain.com # 屏蔽 UA → 403
# 限流规则:快速 30 连发,预期部分请求 503/429
for i in {1..30}; do curl -I http://your-domain.com/api/v1/user/info; sleep 0.1; done
# 增长速度恢复验证(bytes/sec)
before=$(stat -c%s /var/log/nginx/access.log); sleep 60
after=$(stat -c%s /var/log/nginx/access.log); echo "Growth: $(( (after-before)/60 )) bytes/sec"
# 请求量/磁盘持续观察
watch -n 10 "df -h /var/log"
ngxtop -l /var/log/nginx/access.log # pip install ngxtop,实时逐请求统计
业务影响检查:应用日志确认正常用户未受影响、监控确认响应时间与错误率无异常、收集用户反馈确认无误伤。
八、深度根因分析(步骤 13)
| 线索 | 动作 |
|---|---|
| 某接口请求量异常 | grep 应用日志确认调用来源与报错(grep "/api/v1/user/info" /var/log/app/app.log \| tail -100) |
| 怀疑数据库瓶颈 | mysqldumpslow -s c -t 10 /var/log/mysql/slow.log 统计慢查询 |
| 全局视角 | Prometheus/Grafana 四层指标:Nginx(QPS/响应时间/错误率/连接数)、系统(CPU/内存/IO/带宽)、应用(接口耗时/连接池/缓存命中率)、业务(活跃/订单/支付) |
| 怀疑网络层攻击 | tcpdump -i eth0 -w /tmp/capture.pcap port 80 or port 443 + Wireshark 查 SYN Flood/HTTP Flood/Slowloris/大量重传 |
| 用了 CDN | 查缓存规则、回源 Host、缓存穿透/雪崩 |
| 业务循环请求 | 查客户端重试逻辑、轮询间隔、死循环、递归调用 |
九、回滚预案(步骤 14)
动手前先备份(时间戳命名):
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
- 回滚屏蔽:注释/删除 block_ip.conf 的 deny 行(或
> /etc/nginx/conf.d/block_ip.conf清空黑名单),nginx -t && nginx -s reload。 - 回滚限流:注释 limit_req/limit_conn 行后重载;恢复时去掉注释。
- 回滚日志过滤:删除 access_log off / if=$loggable 后重载。
- 紧急回滚全部:恢复时间戳备份;备份也没有时用发行版默认配置(
/usr/share/nginx/conf/nginx.conf.default)或yum reinstall nginx/apt-get install --reinstall nginx-common。 - 恢复备份后
nginx -t失败 → 继续恢复更早一版备份,直到语法通过。
十、五条典型排查路径
| 路径 | 特征链 | 处理 |
|---|---|---|
| ① 业务逻辑 | 高频 URI 是正常业务接口 | 查应用日志与调用来源 → 修客户端循环/重试 → 配限流防复发 |
| ② 爬虫流量 | 高频 IP + 爬虫 UA | 查归属与 robots 许可 → 恶意爬虫屏蔽 IP/段;正常爬虫过频则限流 + 日志过滤 |
| ③ 扫描器 | 404 高频敏感路径 | 屏蔽扫描 IP → Nginx 屏蔽敏感路径 → 部署 WAF |
| ④ 健康检查过频 | /health 请求量异常 | 查监控/K8s LivenessProbe·ReadinessProbe/负载均衡探测频率 → 调整频率 + access_log off |
| ⑤ CDN 回源异常 | 来源 IP 是 CDN 节点 | 查缓存规则/回源 Host/穿透雪崩 → 提命中率 + 源站限流 |
十一、四类操作风险红线
- 屏蔽 IP:CDN 节点/公司出口/运营商 NAT 误伤整片用户 → 优先限流不屏蔽、XFF 判真实 IP、设屏蔽有效期、记录屏蔽原因。
- 限流:过严误伤正常用户、配错全拒、突发流量误触发 → 按业务基线设阈值、burst 兜突发、接口差异化、灰度发布。
- 日志过滤:配错导致全量不记、审计合规缺口 → 只过滤明确低价值(健康检查/静态),保留业务接口与 4xx/5xx 日志,定期复查。
- 日志清理:rm 导致写入失效与空间不释放、清理过度无法追溯 → 用 logrotate 自动轮转、紧急用
> file不用 rm、清理前转储对象存储、至少保留 7 天。
十二、生产变更纪律
- 变更前:备份配置、测试环境验证、多机先灰度一台、业务低峰期操作、通知业务/监控/运维相关方。
- 变更中:tail -f access.log 与 error.log、盯 QPS/响应时间/错误率与业务指标、随时准备回滚。
- 变更后:功能验证(关键页面/接口)、性能验证(响应时间无劣化)、日志验证(格式与完整性)、监控验证、收集用户反馈。
- 权限:日志 640 www-data、配置 644 root、含密码密钥的配置 600;配置变更全部留审计记录。
- 长期维护:每周查日志大小/磁盘/告警,每月复查屏蔽·限流·过滤规则,每季度演练故障恢复,及时升级 Nginx 修安全漏洞。
速查:核心命令
| 目的 | 命令 |
|---|---|
| 行数基线 | wc -l access.log |
| Top URI / IP / UA | awk '{print $7}' access.log \| sort \| uniq -c \| sort -rn \| head -20(IP 用 $1;UA 用 awk -F'"' '{print $6}') |
| 状态码分布 | awk '{print $9}' access.log \| sort \| uniq -c \| sort -rn |
| 实时 QPS | tail -f access.log \| pv -l -i 1 -r > /dev/null |
| 轮转调试/状态 | logrotate -d /etc/logrotate.d/nginx;cat /var/lib/logrotate/status \| grep nginx |
| 最大文件 | du -ah /var/log/nginx/ \| sort -rh \| head -10 |
核心要点:快速定位(URI/IP/UA 三高频)→ 分类处理(正常/爬虫/恶意)→ 止血为先(限流优先于屏蔽)→ 深度分析(应用/DB/监控/抓包)→ 长期防护(过滤/轮转/WAF/告警)→ 风险意识(备份、测试、灰度、验证、可回滚)→ 持续优化(定期复查规则与演练)。
关联页面
| 页面 | 关联点 |
|---|---|
| nginx-log-analysis-troubleshooting-guide | 日志字段/超时/4xx/5xx 知识域手册(本页是日志暴涨场景 runbook,互为经纬) |
| nginx-log-analysis-monitoring-guide | 日志分析与监控体系(GoAccess/ELK/告警脚本,本页步骤 11 告警体系的落点) |
| nginx-security-config-guide | 安全配置实战(真实 IP/分级限流/WAF maps,本页步骤 9/11 的深度配置版) |
| nginx-troubleshooting-methodology-8-steps | Nginx 故障排查通用八步方法论(本页是其中"日志/流量异常"场景的展开) |
| tcp-connection-spike-troubleshooting | TCP 连接数暴涨排查(姊妹场景:连接层 vs 请求层的异常流量鉴别) |
| linux-deleted-file-disk-space-reclaim | 幽灵文件与磁盘空间回收(本页步骤 10 紧急清理的机制详解) |
| nginx-config-pitfalls | Nginx 配置踩坑(真实 IP 与核心接口未限流两个坑与本页止血动作直接相关) |
| nginx-502-504-connection-reset-guide | 502/504 深度排查(本页步骤 6 发现 5xx 高频后的下游排查页) |