Nginx的limit_req不直接生成黑名单,需结合warn级日志标记429请求,再通过脚本解析日志提取高频IP,最终用firewalld+ipset或Nginx deny动态封禁,并辅以白名单和自动清理机制防误封。

在 Nginx 中,limit_req 本身不直接生成黑名单,但它能精准识别高频恶意请求,并通过日志标记这些行为——这是构建自动化封禁流程的关键起点。真正实现“黑名单”需分两步:一是用 limit_req 触发可识别的日志条目;二是借助外部脚本(如 shell 或 Python)解析日志、提取 IP、写入防火墙或 Nginx 配置。
一、配置 limit_req 并输出可筛选的拦截日志
核心是让被限速的请求留下明确、易过滤的日志痕迹,避免和正常 503 或其他错误混淆:
- 使用
limit_req_log_level warn将拦截日志设为 warn 级别(默认是 error),这样可与后端故障日志区分开 - 配合
limit_req_status 429返回标准状态码,语义清晰且便于日志归类 - 确保日志格式包含
$status和$request_time,方便后续判断是否为限速触发
示例配置:
http {
# 定义登录接口专用限速区:每分钟最多5次,突发容忍3次,不排队
limit_req_zone $binary_remote_addr zone=login_limit:5m rate=5r/m;
<pre class="brush:php;toolbar:false;">log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log main;}
server { location = /api/v1/login { limit_req zone=login_limit burst=3 nodelay; limit_req_log_level warn; limit_req_status 429;
proxy_pass http://backend;
proxy_set_header Host $host;
}}
二、从日志中准确提取高频访问 IP
不是所有 429 都该拉黑,要过滤出“持续高频”的真实攻击者。建议按以下逻辑筛选:
- 只统计
status=429且request_time极小(如 - 用
awk + sort + uniq -c统计单位时间(如 5 分钟)内触发 429 超过 10 次的 IP - 排除已知爬虫 UA 或可信网段(如运维出口、CDN 回源段)
简易 shell 提取命令(每 5 分钟运行一次):
# 提取最近5分钟内触发429超15次的IP(排除内网和常见爬虫)
awk -v d="$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M:%S')" \
'$4 ~ /\[.*'"$d"'[^\]]*\]/ && $9==429 && $12 15 {print $2}' > /tmp/suspicious_ips
三、将 IP 加入黑名单的两种可靠方式
推荐优先使用防火墙(如 firewalld + ipset),比修改 Nginx 配置更轻量、热生效、不 reload:
-
firewalld + ipset 方式:创建
badipsipset,添加 rich-rule 拦截 HTTP/HTTPS 流量,再用firewall-cmd --reload生效 -
Nginx deny 方式:动态追加
deny 1.2.3.4;到http或server块,但需nginx -s reload,有轻微抖动风险
firewalld 示例(一次性初始化后,后续只需 add-entry):
# 首次运行(仅需一次) firewall-cmd --permanent --new-ipset=badips --type=hash:ip firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source ipset="badips" service name="http" drop' firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source ipset="badips" service name="https" drop' <h1>封禁新IP(从 /tmp/suspicious_ips 读取)</h1><p>while read ip; do firewall-cmd --permanent --ipset=badips --add-entry="$ip" done </p><p>firewall-cmd --reload</p>
四、规避误封的关键细节
生产环境最怕封错客户或搜索引擎,必须做前置过滤:
- 在日志分析脚本开头,先查
whois或用公开库反查 IP 归属,自动跳过主流云厂商、CDN(如 Cloudflare、阿里云全站加速)出口段 - 维护一个白名单文件(如
/etc/nginx/whitelist.conf),用geo+map指令绕过限速,例如:geo $whitelist_ip { default 0; include /etc/nginx/whitelist.conf; }<br> map $whitelist_ip $login_limit_key { 0 $binary_remote_addr; 1 ""; }
再把limit_req_zone的 key 改成$login_limit_key - 封禁后保留 24 小时,用定时任务自动清理过期条目(避免长期累积)











