Nginx日志中大量无效请求需区分真伪异常,核心是识别行为模式而非单纯计数:预连接导致的400、空请求线导致的“-”字段、用户刷新引发的499属正常;而固定IP高频4xx/5xx、异常UA(如sqlmap、python-requests)、非常规路径(如/phpmyadmin/)集中访问才需干预。

排查 Nginx 日志中大量无效请求的来源,核心是区分“真异常”和“伪异常”,避免把浏览器预连接、客户端重试、爬虫试探等正常行为误判为攻击或故障。重点不在数日志行数,而在识别请求模式与上下文关联。
先确认哪些“无效请求”其实不真无效
很多看似异常的请求,其实是客户端(尤其是现代浏览器)的正常行为:
-
HTTP 400 错误高频出现?很可能是 Chrome 等浏览器的 pre-connection(预连接):它会提前建 5–6 个空 TCP 连接,用不完就直接断开——Nginx 收到未发任何 HTTP 请求的连接,记录为 400,但实际无害;可通过 telnet 测试复现(
telnet your-server 80后立即退出) -
大量“-”字段请求(如
"-" 400 0 "-" "-")?说明 request line 完全缺失,基本可判定为连接建立后未发送数据,属于连接层干扰,不是应用层攻击 - 短时间密集 499(Client Closed Request)?常因用户快速刷新、前端取消 AJAX 请求、或移动端网络切换导致,需结合前端埋点或监控交叉判断,不宜直接封禁 IP
聚焦真正可疑的请求特征
真正需要干预的无效请求,通常伴随可识别的行为模式:
-
固定 IP + 高频 4xx/5xx:用
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -20找出 Top 访问 IP,再过滤其对应请求:grep "192.168.1.100" access.log | awk '$9 ~ /^[45][0-9][0-9]$/ {print $7,$9,$11}' | sort | uniq -c | sort -nr -
异常 User-Agent 或无 UA:如
curl、python-requests、空字符串、或含扫描关键词(sqlmap、nuclei、gobuster) -
非常规路径集中爆发:比如大量访问
/phpmyadmin/、/wp-admin/、、<code> 等,大概率是自动化探测 -
超大 body 或 header:配合 error.log 中
client intended to send too large body或request header or cookie too large报错,说明存在恶意构造请求
用时间+请求上下文锁定源头
单看某条日志意义有限,必须做时间轴关联分析:
- 在 error.log 出现
worker process X exited on signal 9或upstream timed out的时间点前后 30 秒内,查 access.log:awk -v start="27/Jul/2026:14:22:00" -v end="27/Jul/2026:14:22:30" '$4 >= "["start"]" && $4 - 统计同一 IP 在该时段的
$request_time(需日志含该变量):若大量请求耗时极短( - 检查是否集中在某个
server_name或location块:说明问题可能出在特定虚拟主机或代理配置,而非全局
验证并隔离,而不是盲目封禁
发现可疑 IP 后,别急着加防火墙规则,先做最小验证:
- 临时用
limit_req限速测试:limit_req zone=burst burst=5 nodelay;,观察是否影响业务,同时看 error.log 是否仍有新错误 - 用
curl -v -H "User-Agent: test-bot" http://your-site/test-path模拟请求,确认是否真被拦截,排除 CDN 或 WAF 干预干扰 - 如果确认是恶意扫描,优先在 Nginx 层 return 444(直接关闭连接,不返回任何响应),比 403/404 更节省资源


















