nginx基于行为的自动化封禁通过limit_req限流、map/geo动态标记恶意行为、日志分析+外部脚本拉黑、redis实时封禁四层机制实现。先用limit_req_zone定义每ip 5r/s速率限制,burst=10允许突发;再用map匹配ua、uri、method等特征赋值变量,if判断后return 444;接着配置攻击日志格式,由脚本解析高频5xx、爆破登录等行为,生成deny规则并reload;最后进阶接入redis,用lua脚本incr/expiry计数并拦截,避免reload开销,支持分布式。

通过 Nginx 实现基于行为的自动化封禁,核心在于结合 limit_req、geo/map、日志分析与外部脚本联动,对高频访问、异常路径、爬虫特征等行为进行实时识别与动态拦截,而非仅依赖静态 IP 黑名单。
利用 limit_req 控制请求频次
这是最常用且零依赖的内置方案。通过定义请求速率阈值,对超出限制的客户端返回 503 或重定向,适用于防暴力刷接口、注册/登录爆破等场景。
- 在
http块中定义共享内存区域:limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s; - 在
location中启用限流:limit_req zone=perip burst=10 nodelay;(允许突发 10 次,不延迟) - 配合
limit_req_status 429返回标准限流状态码,便于前端或监控识别
用 map + geo 动态标记高危行为
将日志中暴露的恶意模式(如 UA 含 sqlmap、路径含 /wp-admin 但非登录页、Referer 为空却大量 POST)映射为变量,在 server 或 location 中直接拒绝。
- 定义匹配规则:
map $http_user_agent $is_bad_ua {<br> ~*(sqlmap|nikto|dirb) 1;<br> default 0;<br>} - 在 location 中拦截:
if ($is_bad_ua) { return 444; }(444 是 Nginx 特有“关闭连接”状态) - 结合
$request_uri和$request_method可识别扫描行为,例如:map "$request_method:$request_uri" $is_scan {<br> ~^POST:/api/login.* 0;<br> ~^GET:/admin.* 1;<br>}
通过日志 + 外部脚本实现自动拉黑
Nginx 自身不支持写黑名单文件,需借助日志输出 + 定时/实时脚本解析,再重载配置或更新 GeoIP 白名单文件。
- 配置自定义日志格式,包含关键字段:
log_format attack '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time $upstream_response_time'; - 用 awk / Python 脚本定时扫描最近 1 分钟日志,统计:单 IP 5xx 错误 ≥10 次、POST /login 失败 ≥5 次、UA 为空且路径含
/wp-等 - 生成或追加 IP 到
/etc/nginx/conf.d/blacklist.conf:deny 192.168.1.100;<br>deny 203.0.113.45;
- 脚本末尾执行
nginx -t && nginx -s reload生效(注意 reload 频率不宜过高)
进阶:对接 Redis 实现实时封禁
避免频繁 reload,用 ngx_http_redis_module(需编译)或 OpenResty 的 lua-resty-redis,在 Lua 脚本中查 Redis 黑名单或计数器。
- 记录每个 IP 的错误次数到 Redis:
redis.incr("block:ip:" .. client_ip)<br>redis.expire("block:ip:" .. client_ip, 300) - 请求前检查:
if redis.get("block:" .. client_ip) == "1" then return ngx.exit(403) end - 失败达阈值时写入 Redis,比文件方案更轻量、支持分布式部署











