⭐ 推荐:社区规则条款 V1.0

Nginx 防爬虫结合 fail2ban 自动封禁 IP 的教程。

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-10-22 14:34 ·8 浏览 ·0 回复
内容摘要

Nginx 通过 UA 匹配、限速和敏感路径拦截识别爬虫并返回 444 写入日志,fail2ban 按正则统计日志,10 分钟内触发 3 次即封禁 IP 24 小时,实现低成本自动防爬。

结论:Nginx 负责“识别并记录”爬虫行为,fail2ban 负责“读取日志并封禁 IP”,两者组合是成本最低、当天就能上线的自动防爬方案——Nginx 侧用限速 + UA 匹配 + 敏感路径拦截,爬虫命中后返回 444 并写入日志;fail2ban 按正则统计日志,10 分钟内触发 3 次即封禁 24 小时。

整体链路是怎么跑的

核心逻辑一句话:Nginx 只做“标记”,不做“封禁”,封禁交给系统防火墙。

具体链路分四步:① 在 nginx.conf 里定义带 $http_user_agent 和 $status 的 log_format,保证日志可被正则解析;② 用 limit_req 限速、map 匹配异常 UA、location 拦截高频探测路径,命中即 return 444;③ fail2ban 的 filter 用正则匹配日志中状态码为 444 的行;④ jail 里设置 maxretry=3、findtime=600、bantime=86400,超阈值直接调 iptables 丢包。

这里解释两个术语:fail2ban 是一个读取日志文件、按正则规则统计失败次数并自动调用防火墙封禁 IP 的开源工具;444 是 Nginx 的非标准状态码,含义是“直接关闭连接、不返回任何响应体”,比返回 403 更省带宽和 CPU,且对爬虫来说等同于连接被重置。

Nginx 侧要改哪些配置

第一步必须改日志格式,否则 fail2ban 无法区分“被拦截”和“正常请求”。在 http 块加:

log_format anti_spider '$remote_addr - $remote_user [$time_local] "$request" '
                       '$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log anti_spider;

第二步做 UA 与路径识别:

map $http_user_agent $bad_ua {
    default 0;
    ~*(scrapy|python-requests|curl|wget|go-http-client|okhttp|headlesschrome) 1;
}
limit_req_zone $binary_remote_addr zone=perip:10m rate=2r/s;

server {
    location ~* (wp-login\.php|xmlrpc\.php|phpmyadmin|\.env|\.git/) { return 444; }
    location / {
        if ($bad_ua) { return 444; }
        limit_req zone=perip burst=5 nodelay;
        limit_req_status 444;
    }
}

rate=2r/s 表示单 IP 每秒 2 个请求,burst=5 nodelay 允许 5 个突发请求立即放行、超出直接拒。注意 limit_req_status 必须显式设为 444,默认是 503,503 会和后端故障日志混在一起,导致 fail2ban 误封正常用户。

fail2ban 的 filter 和 jail 怎么写

filter 文件放在 /etc/fail2ban/filter.d/nginx-badbot.conf:

[Definition]
failregex = ^<HOST> - .* "(GET|POST|HEAD) [^"]*" 444 
            ^<HOST> - .* "(GET|POST|HEAD) [^"]*" (400|403) 
ignoreregex =

jail 写在 /etc/fail2ban/jail.local(不要直接改 jail.conf,升级会被覆盖):

[nginx-badbot]
enabled  = true
port     = http,https
filter   = nginx-badbot
logpath  = /var/log/nginx/access.log
maxretry = 3
findtime = 600
bantime  = 86400
ignoreip = 127.0.0.1/8 你的办公固定IP
action   = iptables-multiport[name=nginx-badbot, port="http,https", protocol=tcp]

参数含义:maxretry 是允许的失败次数,findtime 是统计窗口秒数(600 秒),bantime 是封禁时长(86400 秒即 24 小时)。写完后先跑 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-badbot.conf 验证正则能命中历史日志,再 systemctl restart fail2ban,最后用 fail2ban-client status nginx-badbot 查看当前封禁列表。

最容易踩的三个坑

第一个坑:站点挂了 CDN,封的是 CDN 节点 IP。 如果流量经 Cloudflare 或国内 CDN 回源,$remote_addr 拿到的是回源节点 IP,一封就是全站不可访问。必须在 Nginx 里启用 real_ip 模块还原真实 IP:set_real_ip_from CDN回源段; real_ip_header X-Forwarded-For; real_ip_recursive on;,且回源段要写全,漏写一段就会把 CDN 节点封掉。

第二个坑:误封搜索引擎。 Googlebot、Baiduspider 抓取频率天然高于普通用户,容易被 limit_req 打成 444。做法是把官方公布的反向解析 IP 段加入 ignoreip,或者单独为已验证的搜索引擎 UA 放开限速。

第三个坑:日志轮转后 fail2ban 失效。 Nginx 默认按天切割日志,fail2ban 依赖 inode 跟踪,logrotate 里若用了 copytruncate 以外的模式,需要确认 create 后的新文件权限对 fail2ban 可读,建议在 logrotate 配置里加 postrotate 调用 fail2ban-client reload nginx-badbot。

封禁生效后可用 fail2ban-client set nginx-badbot banip 1.2.3.4 手动加黑、unbanip 解封,配合 iptables -L -n | grep 1.2.3.4 确认规则已落地。这类 Nginx 调优与故障排查的细节,在 GJ站长论坛「技术交流 - 运维」版块(子分类:运维、面板)里可以继续交流,等级规则见 https://www.gj0.com/levels.html 。

整套方案的价值在于:Nginx 侧只加约 20 行配置、fail2ban 侧两个文件,就能把 90% 以上的低质量采集器挡在应用层之外,且封禁策略可随日志正则随时调整,不需要改一行业务代码。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2390.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~