如何在Nginx中实现动态封禁IP?三种主流方案
本文对比了三种Nginx动态封禁IP的方案:1)原生Nginx通过geo和map模块结合配置文件,需reload生效;2)OpenResty+Lua实现毫秒级实时封禁,支持API管理;3)fail2ban基于日志分析自动封禁,适合防御扫描爆破。方案一简单可靠但需reload,方案二适合自动化系统,方案三适用于安全防护场景。建议根据实际需求选择:手动封禁选方案一,程序化调用选方案二,自动防御选方案三
在运维和安全实践中,经常需要根据实时情况封禁某些恶意 IP。但传统的Nginx 配置是静态的——一旦写死 deny 1.2.3.4;,就必须重启或重载服务才能生效。那么,有没有办法在不中断服务的前提下,实现动态、实时、可编程的 IP 封禁呢?
一、方案对比
| 方案 | 是否需 reload | 实时性 | 技术栈 | 适用场景 |
|---|---|---|---|---|
| geo + map + 文件 | 是(nginx -s reload) |
秒~分钟级 | 原生 Nginx | 手动或定时脚本封禁 |
| OpenResty + Lua | 否 | 毫秒级 | OpenResty(Nginx + Lua) | 自动化、高并发、API 化管理 |
| fail2ban + 日志分析 | 否(系统级封禁) | 秒级 | fail2ban + iptables | 安全防护、日志驱动型封禁 |
二、方案一:原生 Nginx + 外部文件(简单可靠)
适用于不需要极高实时性的场景,比如每天批量封禁一批扫描 IP。
1. 创建封禁 IP 列表文件
# /etc/nginx/conf.d/blockips.conf
192.168.1.100 1;
203.0.113.5 1;
格式为:IP 值;,值通常设为 1 表示“命中”。
2. 在 nginx.conf 中配置
http {
# 从外部文件加载黑名单
geo $block_ip {
default 0;
include /etc/nginx/conf.d/blockips.conf;
}
map $block_ip $deny_ip {
1 "denied";
0 "";
}
server {
listen 80;
if ($deny_ip = "denied") {
return 403;
}
location / {
# 正常业务
}
}
}
3. 动态更新方式
- 修改
blockips.conf - 执行平滑重载:
nginx -s reload
✅ 优点:无需额外依赖,配置简单。
⚠️ 缺点:每次更新需 reload,不适合高频操作。
三、方案二:OpenResty + Lua(推荐用于自动化)
如果你需要通过 API 实时封禁 IP(例如 WAF、风控系统调用),OpenResty 是最佳选择。它基于 Nginx,嵌入 Lua 脚本引擎,支持共享内存,无需 reload 即可生效。
1. 安装 OpenResty
# Ubuntu 示例
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
echo "deb http://openresty.org/package/debian $(lsb_release -sc) openresty" | sudo tee /etc/apt/sources.list.d/openresty.list
sudo apt update && sudo apt install openresty
2. 配置 nginx.conf
http {
lua_shared_dict ip_blacklist 10m; # 共享内存存储黑名单
server {
listen 80;
# 【核心】访问前检查 IP
access_by_lua_block {
local ip = ngx.var.remote_addr
if ngx.shared.ip_blacklist:get(ip) then
ngx.log(ngx.WARN, "Blocked IP: ", ip)
ngx.exit(403)
end
}
# 动态封禁接口(建议加 IP 白名单保护)
location = /ban {
content_by_lua_block {
local args = ngx.req.get_uri_args()
local ip = args.ip
local seconds = tonumber(args.seconds) or 3600
if not ip or not ip:match("^%d+%.%d+%.%d+%.%d+$") then
ngx.status = 400
ngx.say("Invalid or missing 'ip'")
return
end
ngx.shared.ip_blacklist:set(ip, true, seconds)
ngx.say("Banned ", ip, " for ", seconds, "s")
}
}
# 解封 & 查询接口
location / { echo "Hello! Your IP: $remote_addr"; }
}
}
3. 使用示例
# 封禁 IP 5 分钟
curl "http://your-server/ban?ip=1.2.3.4&seconds=300"
# 测试是否被封
curl http://your-server/ # 返回 403
✅ 优点:毫秒级生效、支持 TTL 自动过期、可集成到自动化系统。
🔐 安全提示:务必限制/ban接口的访问来源!
四、方案三:fail2ban + Nginx 日志(自动防御)
适用于防御暴力破解、高频 404 扫描等行为。fail2ban 会监控日志,自动封禁异常 IP。
1. 确保 Nginx 记录详细日志
log_format main '$remote_addr - $remote_user [$time_local] "$request" $status ...';
access_log /var/log/nginx/access.log main;
2. 创建过滤器 /etc/fail2ban/filter.d/nginx-bad-request.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST).*" 404 .*
ignoreregex =
3. 配置 jail(/etc/fail2ban/jail.local)
[nginx-bad-request]
enabled = true
port = http,https
filter = nginx-bad-request
logpath = /var/log/nginx/access.log
maxretry = 10 # 10 次失败
findtime = 600 # 在 10 分钟内
bantime = 3600 # 封 1 小时
action = iptables-multiport[name=nginx, port="http,https"]
4. 启动并查看状态
sudo systemctl start fail2ban
sudo fail2ban-client status nginx-bad-request
✅ 优点:全自动、成熟稳定、社区支持好。
⚠️ 注意:默认使用 iptables 封禁,影响整个服务器,非仅 Nginx。
五、如何选择?
- 只想偶尔手动封几个 IP? → 用 方案一(原生 Nginx)。
- 需要程序自动封禁、支持 API 调用? → 用 方案二(OpenResty)。
- 想自动防御扫描、爆破? → 用 方案三(fail2ban)。
💡 进阶技巧:可让 fail2ban 调用 OpenResty 的
/ban接口,实现“仅封 Nginx + 自动过期”的完美组合。

更多推荐


所有评论(0)