必须配置limit_req_log_level和limit_conn_log_level提升日志级别,并在log_format中加入$limit_req_status等变量,才能准确定位被限流请求的ip、路径及原因,否则默认日志不记录限流事件。

要看到 Nginx 限流拦截了哪些请求、来自哪个 IP、为什么被拦,不能依赖默认日志——它不记录限流拒绝事件。必须主动开启对应模块的日志级别,并配合自定义日志格式才能定位问题。
开启限流相关日志
限流行为由两个独立模块触发:limit_req(控请求数)和 limit_conn(控并发连接数),它们默认几乎不打日志,需显式配置:
- limit_req 拒绝日志:在 http 或 server 块中添加 limit_req_log_level error;(也可设为 warn),被限速丢弃或返回 503 的请求会记入 error.log
- limit_conn 拒绝日志:添加 limit_conn_log_level error;,连接数超限时才会写日志;不加这行,即使配置了 limit_conn,error.log 里也看不到任何拒绝记录
- 两项都建议搭配 limit_req_status 503; 和 limit_conn_status 503;,确保返回统一状态码,方便后续日志筛选
让 access.log 记录更关键的上下文
仅靠 error.log 不够直观,因为不包含请求路径、User-Agent 等信息。需扩展 access.log 格式,把限流决策相关字段带上:
- 在 http 块中定义新日志格式,例如:
log_format limited '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time $limit_rate $limit_conn_status'; - 在对应 location 中启用:
access_log /var/log/nginx/limited_access.log limited; - 关键字段说明:
– $limit_rate 可反映是否触发了下载限速
– $limit_conn_status 在连接被拒时显示非空值(如 “503”)
– 配合 $request_time 能识别慢请求是否卡在限流队列里
从日志里快速定位被拦请求
日志有了,下一步是高效提取有效信息。常用命令示例(假设日志路径为 /var/log/nginx/error.log):
- 查所有因限流返回 503 的记录:
grep "limiting requests" /var/log/nginx/error.log | grep "503" - 提取高频被限 IP(按 error.log 中的 IP 统计):
awk '/limiting requests/ {print $2}' /var/log/nginx/error.log | sort | uniq -c | sort -nr | head -10 - 结合 access.log 查某 IP 的完整行为链:
awk '$1 == "192.168.1.100" {print $0}' /var/log/nginx/limited_access.log - 筛出 burst 队列溢出导致的立即拒绝(含 nodelay 场景):
grep "excess" /var/log/nginx/error.log(Nginx 内部提示“excess request”即超出 burst 容量)
注意真实客户端 IP 的准确性
如果服务前有 CDN 或反向代理,$remote_addr 会变成代理地址,导致限流和日志统计失真:
- 确保 Nginx 正确读取真实 IP,通常需在 server 块开头加:
set_real_ip_from 103.21.244.0/22; # 示例 Cloudflare IP 段
real_ip_header X-Forwarded-For; - 然后将限流 key 改为 $binary_remote_addr(内存更省)或 $http_x_forwarded_for(需清洗多层代理头)
- 否则你看到的“高频攻击 IP”可能全是同一个 CDN 节点,实际源头完全没被识别











