文章总结: 本文系统介绍了Nginx日志分析的核心方法,涵盖自定义logformat、使用awk/grep/sort/uniq等命令行工具高效定位异常请求与攻击源。文章强调通过request_time与upstreamresponsetime对比区分慢请求根因,并给出CC攻击、扫描器、SQL注入的日志特征及基于limitreq/geo/map的封禁思路。内容基于一线实战经验,提供了从日志解读到攻击阻断的完整排查路径,可操作性强。 综合评分: 85 文章分类: 安全运营,WEB安全,应急响应,实战经验,安全工具
步骤 5:按时段看 5xx 突增位置
目的:找到 5xx 集中爆发的时间窗。
命令(按分钟聚合 5xx):
awk 'BEGIN{IGNORECASE=1} /"status":[5-9][0-9][0-9]| [5-9][0-9][0-9] / {
match($0, /\[([0-9]{2}\/[A-Za-z]+\/[0-9]{4}):([0-9]{2}):([0-9]{2}):/, m)
minute = m[1]":"m[2]
count[minute]++
}
END {for (k in count) print count[k], k}' /var/log/nginx/access.log \
| sort -nr | head -n 20
这一段思路给读者,节奏跟随”分钟粒度”即可,复杂脚本要严格走审校。
预期输出:
2302 08/Jul/2026:14:21
1810 08/Jul/2026:14:20
...
异常表现:
- 集中在某个时间点 → 对应业务操作、上线发布、定时任务。
- 缓慢上升 → 后端泄漏或资源耗尽。
下一步动作:进入步骤 6 看慢请求。
步骤 6:找慢请求
目的:把慢请求(一般是 request_time > 1s)单独拎出来分析。
命令(提取慢请求的 IP、URL、耗时):
awk '{
match($0, /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, ip)
match($0, /\"([A-Z]+) ([^ ]+) HTTP/, req)
match($0, / request_time\":([0-9.]+)| [0-9.]+ /, rt)
if (rt[1] + 0 >= 1) {
print rt[1], ip[1], req[2]
}
}' /var/log/nginx/access.log | sort -nr | head -n 50
这段 gawk 形式因版本差异较大,标准 awk 里
match()第三参数是 GNU awk 扩展。如果不想依赖 gawk,更稳的做法是先用grep抽行,再用awk切列:
grep -E 'request_time":(1[0-9]|[2-9][0-9]|[0-9]{3,})' /var/log/nginx/access.log \
| awk -F'"' '{for(i=1;i<=NF;i++){if($i~/request_time/){rt=$(i+2)}; if($i~/remote_addr/){ip=$(i+2)}; if($i~/request_uri/){uri=$(i+2)}}} rt+0>=1{print rt, ip, uri}' \
| sort -nr | head -n 50
预期输出:类似
3.421 198.51.100.10 /api/order/detail
2.987 198.51.100.10 /api/cart
...
异常表现:
- 同一个 IP 全部慢 → 客户端网络问题(运营商/区域)。
- 同一个 URL 全部慢 → 后端或数据库问题。
- 同一个
$upstream_addr全部慢 → 单台后端故障。
下一步动作:进入步骤 7 看上游状态。
步骤 7:上游 vs Nginx 谁慢
目的:判断瓶颈在后端还是 Nginx 自身。
命令:
awk -F'"' '{for(i=1;i<=NF;i++){if($i~/upstream_response_time/){urt=$(i+2)}; if($i~/request_time/){rt=$(i+2)}; if($i~/status/){st=$(i+2)}; if($i~/upstream_addr/){ua=$(i+2)}} if(urt!="-" && rt+0>=1) print rt, urt, ua, st}' \
/var/log/nginx/access.log | head -n 50
预期输出:类似
3.421 3.300 10.0.0.7:80 200
2.987 2.900 10.0.0.7:80 200
异常表现:
-
rt ≈ urt且均大 → 后端慢。
-
rt远大于
urt→ 写日志/缓冲/压缩/慢客户端,慢在 Nginx 自身。 -
urt=-且
st=502→ Nginx 没连上上游。
下一步动作:进入步骤 8 看错误详情。
步骤 8:error_log 错误码分类
目的:把 error_log 按”原因”聚拢。
命令:
grep -oE '\[(error|warn|crit)\] [0-9+#, ]+\*[0-9]+: .*' /var/log/nginx/error.log \
| grep -oE '(no live upstreams|connect\(\) failed|upstream timed out|SSL_.*|client sent invalid|limiting requests|.*directory.*access denied)' \
| sort | uniq -c | sort -nr | head -n 20
预期输出:类似
182 upstream timed out
41 no live upstreams
12 SSL_do_handshake() failed
7 limiting requests
异常表现:
-
upstream timed out集中 → 调
proxy_*_timeout/ 检查后端。 -
no live upstreams→ 上游都死了,健康检查可能没配或失败。
-
limiting requests→ 触发了限速,确认是否正常业务或被打。
下一步动作:进入步骤 9。
步骤 9:识别攻击类型
目的:识别 CC、扫描器、SQL 注入几类攻击。
命令(CC):
awk '{c[$1]++} END {for (k in c) if (c[k] >= 1000) print c[k], k}' /var/log/nginx/access.log \
| sort -nr | head
上面统计的是按行首字段计数。如果是 JSON:
cat access.log | jq -r '.remote_addr' | sort | uniq -c | sort -nr | awk '$1 >= 1000' | head
命令(扫描器):
grep -E '/(admin|wp-login|phpmyadmin|\.env|\.git|backup|\.sql|\.bak)/?(\$|\?|#|)' /var/log/nginx/access.log \
| awk -F'"' '{print $6}' | sort | uniq -c | sort -nr | head
$6在combined格式里通常是$http_user_agent。
命令(SQL 注入特征):
grep -Ei 'union(\s|%20)+select|select(\s|%20)+\*|or(\s|%20)+1(=|%3d)1|benchmark\(|sleep\(|information_schema' /var/log/nginx/access.log \
| awk -F'"' '{print $1}' | sort | uniq -c | sort -nr | head
预期输出:CC 上千、扫描器多条命中特定 UA、SQL 注入几条但可能 IP 集中。
下一步动作:进入步骤 10 处置。
步骤 10:用 limit_req / map / geo 处置
目的:把高频攻击源阻断。
配置(/etc/nginx/conf.d/security.conf):
# 限速:每秒 30 个请求,burst 60,限后队列延迟处理
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=30r/s;
# UA 黑名单
map $http_user_agent $bad_ua {
default 0;
"~*sqlmap" 1;
"~*nmap" 1;
"~*masscan" 1;
"~*(curl|wget|python-requests)" 1;
"~^$" 1; # 空 UA
}
# 按国家封禁(举例,需合规评估)
# geoip2 模块需要额外编译安装,这里以传统 geo 给出思路
geo $geo_country_code $is_high_risk {
default 0;
XX 1; # 按需
}
server {
listen 80;
server_name _;
# 全局限制
limit_req zone=req_zone burst=60 nodelay;
# UA 黑名单直接 403
if ($bad_ua) {
return 403;
}
# 高风险国家直接 403(仅举例)
if ($is_high_risk) {
return 403;
}
location / {
proxy_pass http://backend;
include /etc/nginx/proxy_params;
}
}
/etc/nginx/proxy_params:
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
生效与验证:
nginx -t
nginx -s reload
tail -f /var/log/nginx/access.log | grep -E ' 403 '
异常表现:
- 误杀:业务反馈正常用户拿 403;先去掉
map一项或增加例外。 - 限速失败:可能是
limit_req_zone的 key 不对,反代场景务必用$binary_remote_addr并配合real_ip。
下一步动作:进入步骤 11 验证闭环。
步骤 11:验证闭环
目的:确认处置后攻击流量被挡、误伤为 0。
命令:
# 攻击 IP 后续状态码应为 403
grep '203.0.113.45' /var/log/nginx/access.log | tail -n 50 | awk '{print $9}' | sort | uniq -c
预期:
50 403
业务验证:
- 拿正常用户 UA 不应被误杀。
- 拿 sqlmap UA 试探应 403。
下一步动作:进入步骤 12 做回滚预案与清理。
步骤 12:日志轮转、磁盘占用、清理
目的:日志切走、磁盘不爆、旧日志可归档。
/etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
命令:
# 模拟 logrotate
logrotate -d /etc/logrotate.d/nginx
# 真正切一次
logrotate -f /etc/logrotate.d/nginx
# 磁盘占用
df -h /var/log
# 删除旧日志(务必确认)
find /var/log/nginx -name '*.gz' -mtime +30 -ls
find /var/log/nginx -name '*.gz' -mtime +30 -delete
风险提醒:
-
find -delete不进回收站,先
find ... -ls看一遍。 -
不要直接
rm /var/log/nginx/access.log然后nginx -s reload:当前 fd 还在写旧 inode,磁盘不会释放。先> access.logtruncate,再让 logrotate 接管。 -
不要用
echo "" > access.log在生产强压时段,可能打散日志。 -
日志清理要分批:
find -mtime +30 -delete比find -delete全清要安全。
下一步动作:进入第十三节(生产环境注意事项)做兜底确认。
六、常用命令
把上面用到的命令整合成速查表。每条都标注目的与判断逻辑。
| 命令 | 目的 | 关键判断 |
| — | — | — |
| nginx -V | 看编译参数(--with-debug、模块) | 检查模块是否齐 |
| nginx -t | 测试配置语法 | 改完必跑 |
| nginx -T | 输出完整配置 | 排查默认值差异 |
| nginx -s reload | 平滑重载配置 | 不要 nginx -s restart |
| tail -F /var/log/nginx/access.log | 实时看日志 | ^F 比 -f 更可靠 |
| awk '{print $7}' access.log | sort | uniq -c | sort -nr | head | TOP N URL | 字段位置以 combined 格式为准 |
| grep -E ... | wc -l | 统计某类请求总数 | 配合 cut 做时间窗聚合 |
| grep 'IP' access.log | awk '{print $9}' | sort | uniq -c | 单 IP 状态码分布 | 攻击 vs 正常 vs 限速 |
| cat access.log | python3 -c '...json...' | 处理 JSON 日志 | 不依赖 jq 也能用 |
| jq -r '.remote_addr' access.log | JSON 字段抽取 | 装 jq 即可 |
| grep -E '| 5[0-9][0-9] |' 或对应 JSON 字段 | 5xx 过滤 | 注意要双空格包裹 |
| iptables -L -n -v | grep DROP | 防火墙封禁情况 | 二次确认封禁来源 |
| wc -l access.log | 行数 | 文件大小粗判断 |
每个字段的取值含义,反复出现在第七节配置示例里会再展开。
七、配置示例
下面给一套 Nginx + logrotate + iptables 的完整可落地配置清单。路径默认以 CentOS/RHEL 为准,Debian/Ubuntu 类似。
1. Nginx http 块(含 log_format)
/etc/nginx/nginx.conf:
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
# gzip...
include /etc/nginx/conf.d/gzip.conf;
# 自定义日志
log_format main escape=json
'{'
'"time":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"x_forwarded_for":"$http_x_forwarded_for",'
'"request_method":"$request_method",'
'"request_uri":"$request_uri",'
'"server_name":"$server_name",'
'"request_time":"$request_time",'
'"upstream_addr":"$upstream_addr",'
'"upstream_response_time":"$upstream_response_time",'
'"upstream_connect_time":"$upstream_connect_time",'
'"upstream_status":"$upstream_status",'
'"status":"$status",'
'"bytes_sent":"$body_bytes_sent",'
'"connection_requests":"$connection_requests",'
'"http_user_agent":"$http_user_agent",'
'"http_referer":"$http_referer"'
'}';
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
error_log /var/log/nginx/error.log warn;
# 安全/限速
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=30r/s;
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
limit_req_status 429;
limit_conn_status 429;
map $http_user_agent $bad_ua {
default 0;
"~*sqlmap" 1;
"~*nmap" 1;
"~*masscan" 1;
"~*python-requests" 1;
"~*curl" 1;
"~*wget" 1;
"~^$" 1;
}
include /etc/nginx/conf.d/*.conf;
}
2. server 配置示例(带限速与黑名单)
/etc/nginx/conf.d/site.conf:
upstream backend {
server 10.0.0.7:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.8:8080 max_fails=3 fail_timeout=10s;
keepalive 64;
}
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# UA 黑名单直接 403
if ($bad_ua) {
return 403;
}
# 全局限速
limit_req zone=req_zone burst=60 nodelay;
limit_conn conn_zone 50;
access_log /var/log/nginx/example.access.log main buffer=32k flush=5s;
error_log /var/log/nginx/example.error.log warn;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
}
location ~* \.(js|css|jpg|jpeg|png|gif|ico|svg|woff2?)$ {
expires 7d;
add_header Cache-Control "public, max-age=604800, immutable";
access_log off;
}
}
3. logrotate
/etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
sharedscripts
create 0640 nginx adm
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
4. iptables 兜底(极端情况下给到 L4)
iptables -I INPUT -s 203.0.113.45 -j DROP
iptables -L -n -v | grep DROP
注意:如果是反代,前面有 SLB/CDN,要
iptables -I INPUT -s SLB段 -j DROP会把自己打挂。一般用 Nginx 自己deny即可。需要更彻底就在 WAF/CDN 那一侧封。
八、日志或指标观察方法
Nginx 自身的指标观察主要靠三类:
-
error_log 直接看,每条都有时间戳与原因。
-
access_log 通过分析得到响应时间、状态码分布、TOP IP/URL。
-
stub_status、
vts模块的实时计数器。
1. stub_status 观察并发与连接
/etc/nginx/conf.d/status.conf:
server {
listen 127.0.0.1:8080;
server_name _;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}
命令:
curl -s http://127.0.0.1:8080/nginx_status
输出样例:
Active connections: 120
server accepts handled requests
84321 84321 120932
Reading: 5 Writing: 8 Waiting: 107
判断逻辑:
-
Active connections持续上行:连接堆积。
-
Reading + Writing不大但
Waiting高:长连接等待多,可能被打或被慢客户端吃掉。 -
accepts == handled不成立:被内核 drop 了,调
worker_rlimit_nofile/somaxconn。
2. vts 模块(可选)
如果想看实时面板,按需装:
# 通常需要带 --add-module=/path/to/nginx-module-vts 重新编译 Nginx
不展开安装细节。
3. 日志量监控
每周至少看一眼:
ls -lhS /var/log/nginx/
du -sh /var/log/nginx
异常表现:
- 单日日志 > 几十 G:要么日志格式太冗余,要么被打,要么没有切。
- 单文件超过 2G:分析会很慢。
4. 关键指标告警建议
| 指标 | 计算 | 阈值(示例,按业务调整) |
| — | — | — |
| 5xx 比例 | 5xx_总数 / 总请求数 | > 1% 告警,> 5% 严重 |
| 99 线响应时间 | request_time 99 分位 | > 1s 关注,> 3s 告警 |
| wait 连接数 | Waiting | > worker_connections * 0.8 |
| 磁盘 log 使用率 | du -sh /var/log/nginx | > 80% 告警 |
所有告警阈值都要按业务基线调;高峰期与静默期分开两套阈值更稳。
九、排查路径
下面给一张常见异常的”分类决策树”,结合前面的命令与配置直接对应。每一条都标明在 access_log / error_log 怎么找。
路径 1:网站变慢
-
现象:业务反馈”打开要好几秒”,监控曲线 RT 上升。
-
第一步:error_log 里搜
upstream timed out、connect() failed、no live upstreams。 -
命中 → 进入”上游相关”分支。
-
没命中 → 进入下一步。
-
第二步:access_log 看
$request_timevs$upstream_response_time。 -
接近且都大 → 后端慢。
-
request_time远大 → Nginx 自身(写日志/压缩/缓冲)。
-
第三步:看 worker 数量与连接数(stub_status)。
Waiting高 → 慢连接多。 -
第四步:客户端慢 → 看 TOP 客户端 IP、UA、地理分布。
路径 2:5xx 突增
-
现象:监控告警 5xx。
-
第一步:按分钟聚合 5xx(见步骤 5)。
-
第二步:按状态码 502 / 503 / 504 / 500 / 499 分桶。
-
502 集中 → upstream 拿不到响应(连不上、超时、上游挂)。
-
503 集中 →
service unavailable,一般触发max_fails后upstream块熔断。 -
504 集中 →
upstream timed out。 -
500 集中 → 后端主动报错,跨传。
-
499 集中 → 客户端主动断连接(”Connection: close”或不耐烦),可能慢请求或限速。
-
第三步:对应错误码去 error_log 与后端日志对齐时间点。
路径 3:被打 CC / 扫描
-
现象:连接数打满、带宽打满、某些 URL 高频。
-
第一步:按 IP 找 TOP。
-
第二步:按 URL 特征找是否集中。
-
第三步:按 UA 看是否工具特征。
-
第四步:决策
-
单 IP 集中 →
limit_req_zone或iptables封。 -
UA 集中 →
map $http_user_agent直接 403。 -
路径集中 →
location ~* URL+limit_req。
路径 4:日志写满磁盘
-
现象:磁盘告警,access_log 把 /var/log 写满。
-
第一步:
du -sh /var/log/nginx。 -
第二步:判断 logrotate 是否在跑。
-
logrotate -d /etc/logrotate.d/nginx模拟跑。
-
cat /var/lib/logrotate.status看上次成功时间。
-
第三步:处置
-
临时:
find /var/log/nginx -name '*.log.*.gz' -mtime +3 -ls确认后-delete。 -
长期:
logrotate配rotate 7、compress、notifempty。
路径 5:反代后看到的是代理 IP
- 现象:限速完全失效,所有请求被同一个 IP 抓住。
- 第一步:检查
real_ip_header与set_real_ip_from。 - 第二步:确认 XFF 头是否被中间层剥离。
- 第三步:升级
limit_req_zone的 key 为$binary_remote_addr时,确保real_ip已生效。
路径 6:客户端拿 429
- 现象:正常用户报告”网页打不开”。
- 第一步:access_log 看是否集中到某些 IP。
- 第二步:调小限速阈值或加例外 URI 白名单。
- 第三步:调客户端重试策略(
Retry-After头)。
路径 7:扫描器持续骚扰
- 现象:404/403 持续高频。
- 第一步:合并扫描特征进
map,比如对/wp-login.php这一类直接 444。 - 第二步:把 444 替换为
return 444让 Nginx 直接关连接不回响应。
十、风险提醒
下面列几条极易踩坑的风险点,操作前先看。
1. rm 误删日志
- 风险:
rm /var/log/nginx/access.log+nginx -s reload不会释放磁盘,因为 fd 仍指向旧 inode。 - 替代:
> /var/log/nginx/access.log把文件 truncate 老 inode;或logrotate -f强制切。
2. truncate / > file
- 风险:当前 Nginx master 进程的 fd 仍指向旧 inode;老 inode 被删除释放要等 worker 释放。
- 替代:用
kill -USR1 $(cat /var/run/nginx.pid)让 Nginx 重新打开日志文件,新写入新文件。
3. find -delete 大量清理
- 风险:条件写错就清掉所有生产日志。
- 替代:先
find ... -ls确认列表,再用find ... -print | xargs -I{} echo {}模拟,最后-delete。
4. 限速误杀
- 风险:
limit_req rate=1r/s配burst=0会让大文件下载/同步请求被 429。 - 替代:对静态资源、
GET /static/*单独配置更高的限速阈值或关闭限速。
5. 封禁段错误
- 风险:把 SLB 段封了导致 Nginx 自己都连不上反代上游。
- 替代:先确认内网 IP 段和白名单范围,再封外网段或未知段。
6. reload 配置失败
- 风险:
nginx -t没跑就直接nginx -s reload,旧进程继续跑但新配置已被解析失败,启动会因为nginx: [emerg]滚出告警。 - 替代:始终先
nginx -t,出现test failed不再 reload。
7. 日志格式错乱
- 风险:自定义 log_format 引号/字段拼错,Nginx 重启后下游 ELK 解析失败。
- 替代:用
escape=json配合 JSON 解析;上线前用一条测试请求确认。
8. buffer=flush 太激进的丢日志
- 风险:
buffer=64k flush=60s:60s 内崩溃丢 60s 日志。 - 替代:支付/订单这类按
flush=1s;普通业务按flush=5~10s。
十一、验证方式
每一步处置后都要验证,否则就是”心里没数”。
1. 攻击源被封验证
grep '203.0.113.45' /var/log/nginx/access.log | tail -n 100 | awk '{print $9}' | sort | uniq -c
预期:100 403 持续出现。
2. 慢请求是否回到基线
jq -r '.request_time' /var/log/nginx/access.log \
| awk '{if($1+0 > 1) c++} END{print "slow:", c+0}'
再对比同样方法在优化前的日志,看 99 线。
3. 限速生效
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" http://example.com/api/list; done | sort | uniq -c
预期:看到一部分 200、一部分 429。
4. 配置 reload 成功
nginx -t
nginx -s reload
systemctl status nginx
ps aux | grep nginx | grep -v grep
预期:master PID 不变,worker PID 变化。
5. logrotate 成功
logrotate -d /etc/logrotate.d/nginx
logrotate -f /etc/logrotate.d/nginx
ls -lh /var/log/nginx/
预期:旧文件 .1 / .gz 出现,新文件开头几行新内容。
6. 误杀检查
grep '403' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head
判断:如果 TOP 403 出现自己内部服务 IP、健康检查 IP、白名单 UA,全部都是误杀源。
7. stub_status 峰值恢复
watch -n1 'curl -s http://127.0.0.1:8080/nginx_status'
判断:攻击时 Active connections 上升、攻击缓解后回落到正常基线。
十二、回滚方案
任何改动前都应该先有一个回滚预案。下面是常见变更的回滚方案清单。
1. 配置变更
- 修改前:
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak$(date +%s)。 - 修改后:
nginx -t && nginx -s reload。 - 回滚:
cp /etc/nginx/nginx.conf.bakTIMESTAMP /etc/nginx/nginx.conf && nginx -t && nginx -s reload。
2. limit_req / map 调整
- 修改前:备份当前
map与limit_req_zone。 - 修改后:观察 TOP 403 / 状态码。
- 回滚:直接用备份文件。
3. logrotate 不切日志
- 排查:
logrotate -d。 - 回滚:恢复
postrotate段(USR1)。
4. iptables 误封内网
- 排查:
iptables -L -n -v | grep DROP。 - 回滚:
iptables -D INPUT -s 203.0.113.45 -j DROP。
5. 自定义 log_format 影响下游
- 排查:ELK 解析报错。
- 回滚:把
access_log切回combined:access_log /var/log/nginx/access.log combined;。注意日志格式改变会导致旧解析器失败,建议新格式上线前先在测试集群验证。
6. 日志清理误删
- 排查:日志突然断档。
- 回滚:从归档/异地备份恢复。
十三、生产环境注意事项
下面这些是踩过坑之后的总结,每条都是”曾经被绊倒过一次”。
1. 不要裸跑 > access.log
生产环境不要直接 echo "" > access.log。Nginx master 的 fd 仍指向旧 inode,文件被截空,但 inode 还活着,磁盘占用不变,甚至可能因为日志被截,导致下游 ELK 拿不到完整一条 JSON(不是一行的尾)。最稳的处理:
echo -n "" > /var/log/nginx/access.log # truncate 当前文件内容但不换 inode
kill -USR1 $(cat /var/run/nginx.pid) # 让 Nginx 重新打开日志
2. 日志采样与按业务分流
业务量大时,可考虑按 server_name 或 location 分流多个 access_log,方便按域名排查:
access_log /var/log/nginx/example.access.log main buffer=32k flush=5s;
access_log /var/log/nginx/api.access.log main buffer=32k flush=5s;
3. 反代场景的客户端 IP
反代后面必须有 set_real_ip_from + real_ip_header,否则所有”客户端”看起来都是 SLB 内网 IP。常见可信代理段:
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
set_real_ip_from 100.64.0.0/10;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
上面这些是举例,实际要按你的 SLB/CDN 段写。
4. 高并发场景的 buffer
默认 access_log 每次都 write(),高并发时 IO 抖动。buffer=32k flush=5s 减少 IO 抖动,但最坏情况丢 5s 日志。业务侧必须有损失评估:
- 一般业务:5s 丢日志可接受。
- 支付/订单/计费:5s 丢日志不可接受,调为
buffer=8k flush=1s或不开 buffer。
5. 限速阈值要按业务调
- 大文件上传:阈值要小,否则大请求占满限额。
- 静态资源:可以放更宽。
- 登录页/支付/验证码:需要更严。
6. 日志告警与归档
- 每周把上周日志异地归档一份(OSS/S3)。
- 当前机器只保留 7~14 天。
- 监控
/var/log占用,> 70% 告警。
7. 进程与文件描述符
-
worker_rlimit_nofile:跟
ulimit -n保持一致或更高。 -
/proc/sys/net/core/somaxconn与
net.ipv4.tcp_max_syn_backlog都要相应调高。
8. 安全加固
- 不要把 stub_status 暴露公网。
- access_log 不要写
/tmp等可被任意用户读的目录。 - 控制台的日志归档要走加密传输。
9. 多 Nginx 实例
如果有多实例(比如多个 vhost 一份 nginx 跑,端口不同),每个实例的 PID 路径要配对 logrotate 的 postrotate。
10. 异常时不要直接重启
nginx -s reload 已经足够消解大部分配置错误。systemctl restart nginx 会切断现有长连接,影响业务。除非遇到 worker 异常死循环,否则不要用 restart。
十四、总结
Nginx 日志分析不是花活,靠的是”日志格式打全 + 命令行聚拢 + 限速/封禁处置 + 日志切割与验证”。这一套闭环里:
-
打全字段
:自定义 JSON log_format 把
$request_time、$upstream_response_time、$upstream_addr这些关键字段打出来。 -
聚拢异常
:按 IP、URL、UA、时间窗聚合,定位集中度。
-
证据链收口
:把异常映射到具体机器、具体接口、具体客户端;不要只看现象。
-
处置要稳
:用
limit_req_zone与map兜底;规则要可回滚、可灰度、可验证。 -
日志切割
:logrotate +
USR1信号 + 磁盘告警。 -
验证闭环
:处置后要看监控、状态码、误杀、回落到基线。
只要这套闭环在,每一次线上问题都能在分钟级定位;遇到安全告警,按上面 CC / 扫描 / SQL 注入的分桶把样本抓出来,处置后用封禁策略兜底。
排障熟练度本质上是”日志-命令-配置”三件套的肌肉记忆。把基础命令、字段含义、配置项记熟,每天复盘一次当天的告警,把坏案例写到 wiki,下一次遇到相同 pattern 就能直接走两步到位。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:马哥Linux运维 点击关注 👉 点击关注 👉《Nginx 日志分析技巧:快速找出异常请求和攻击源》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论