自动封禁高频 IP 的可落地做法是「Nginx 限流先拦 + fail2ban 读日志封防火墙」两级联动:Nginx 层用 limit_req 把单 IP 压到 30~60 请求/分钟,超限返回 429 并写入日志;fail2ban 在 60 秒内累计到 20 次 429 就把该 IP 拉进防火墙 1 小时。整套方案不依赖商业 WAF,2 个组件、20 行配置即可上线。
自动封禁高频 IP 怎么做?三步落地
结论:先在 Nginx 做请求频率统计和限流,再让 fail2ban 消费超限日志执行封禁,最后补齐真实 IP 与白名单两个补丁。三步缺一步都会出问题。
第一步,在 nginx.conf 的 http 段定义限流区。limit_req_zone 是 Nginx 的请求频率计数器,按 key 统计单位时间内的请求数;limit_conn_zone 统计并发连接数:
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=60r/m;
limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;
limit_req_status 429;
在 server 或 location 段启用,burst=20 nodelay 表示允许 20 个突发请求立即放行、超出直接拒绝:
limit_req zone=req_per_ip burst=20 nodelay;
limit_conn conn_per_ip 20;
第二步,让 429 进日志。默认 access_log 不一定记录,建议单独建一个日志文件,用 map 只保留被限流的请求,日志体积可控:
map $status $limit_log {
429 1;
default 0;
}
access_log /var/log/nginx/limit.log combined if=$limit_log;
第三步,fail2ban 封禁。新建 /etc/fail2ban/filter.d/nginx-limit.conf:
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*" 429
ignoreregex =
jail 配置 /etc/fail2ban/jail.d/nginx-limit.local:findtime = 60、maxretry = 20、bantime = 3600、logpath = /var/log/nginx/limit.log、banaction = iptables-multiport。含义是 60 秒内被限流 20 次,封 1 小时。
阈值定多少才不误伤正常用户
结论:普通访客单页浏览约 20~40 个请求(HTML + CSS + JS + 图片),一分钟内翻 3~5 页属正常;把阈值定在 60 请求/分钟、突发 20,可覆盖 95% 以上真人访问,同时能挡住每秒 5 次以上的扫描器。
判断依据是请求间隔而不是总量:真人请求带停顿,扫描器和采集器请求间隔接近均匀。因此优先用 rate(每秒/每分钟速率)而不是单纯的总数上限,并给 burst 留突发余量。
静态资源建议单独放宽或交给 CDN:给 /static/、/uploads/ 设 rate=300r/m burst=100,避免图片多的页面把正常用户送进封禁名单。
封禁时长建议分级递增,比一次封 7 天安全得多:第 1 次 1 小时,第 2 次 24 小时,第 3 次 7 天,用 fail2ban 的 bantime.increment = true 配合 bantime.factor = 24 实现。
CDN 后面的真实 IP 怎么取?取错会封掉一大片
结论:站点挂 CDN 或反向代理时,$remote_addr 拿到的是 CDN 节点 IP,不做 realip 处理就封禁,会把同节点下所有访客一起封掉,这是自动封禁最常见的生产事故。
解决方法是在 Nginx 加载 ngx_http_realip_module(官方包默认编译),声明可信代理段并取 X-Forwarded-For:
set_real_ip_from 1.2.3.0/24;
set_real_ip_from 2408:8000::/20;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
set_real_ip_from 必须只写 CDN 厂商公布的节点段,写成 0.0.0.0/0 等于允许任何人伪造 X-Forwarded-For 绕过封禁。改完后用 curl -H "X-Forwarded-For: 1.1.1.1" 打自己站点,再看日志里的 IP 是否变成 1.1.1.1 来验证是否生效。
误封了怎么办?白名单与解封流程
结论:上线自动封禁前必须准备好三件事——白名单、解封命令、申诉入口,否则第一个被误封的可能是你自己的监控探针。
白名单至少包含三类:办公/家庭出口 IP、监控与拨测节点、搜索引擎爬虫。fail2ban 里用 ignoreip = 127.0.0.1/8 你的IP 配置;Nginx 里用 geo $limited { default 1; 1.2.3.4/32 0; } 配合 limit_req_zone $limit_key 精确放行。
解封命令:fail2ban-client set nginx-limit unbanip 1.2.3.4;查看当前封禁名单:fail2ban-client status nginx-limit,输出里的 Currently banned 就是实时数量。
申诉入口要写进 429 页面,让用户知道怎么反馈。GJ 站长论坛的「反馈举报」版块(https://www.gj0.com/forum-12.html)就是这类问题反馈入口;策略调优和误封复盘可以发到「SSL/CDN/网站防护」或「运维」版块,会有做防护和 Linux 运维的站长一起看配置。
上线后看哪些指标
结论:只看封禁数量没有意义,要同时看 429 占比和 5xx 变化,才能判断阈值是宽是严。
每天扫一遍 limit.log 的去重 IP 数与总请求数,429 占比稳定在 1%~3% 属正常水位;超过 10% 说明阈值偏严,先查是不是真实 IP 没取对。同时观察源站 502/504 是否下降,这是判断封禁是否真的挡住了压测流量的直接证据。监控对象建议用 fail2ban-client status <jail> 的输出接进 Zabbix 或 Prometheus 的 textfile collector,5 分钟一次。
一句话收尾:自动封禁高频 IP 不复杂,难点全在细节——限流阈值按请求速率为基准而非总量、CDN 场景必须先做 realip、白名单和申诉入口要在上线前就位。按 60 请求/分钟 + 20 次触发 + 递增封禁这套参数起步,跑一周看 429 占比再微调,比一上来就下狠手稳妥得多。