Nginx + lua-resty-redis 实现毫秒级动态黑名单拦截:请求进入时在 worker 进程内查 Redis,命中即返 403,不回源;需安装 OpenResty 及 lua-resty-redis,用 SET+EXPIRE 存 IP 并自动过期,Nginx 配置连接池与 access_by_lua_block 实时校验,辅以 API 自动封禁。

用 Nginx + lua-resty-redis 做动态黑名单,核心在于:请求进来时快速查 Redis 判断是否被封,命中则立即返回 403,全程在 Nginx worker 进程内完成,不回源、不走后端,真正实现毫秒级拦截。
一、环境准备与模块安装
确保 Nginx 已编译支持 Lua(推荐 OpenResty);lua-resty-redis 是纯 Lua 实现的 Redis 客户端,无需额外 C 依赖,直接下载放到 lua_package_path 指定路径即可。
- 下载地址:https://www.php.cn/link/68db38cc9fc94a0feff812a13896202f
- 将
resty/redis.lua放入/usr/local/openresty/lualib/resty/ - Nginx 配置中添加:
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
二、Redis 黑名单数据结构设计
用 Redis 的 SET 或 Sorted Set 均可,但推荐用 SET + EXPIRE 组合:每条封禁记录为一个 key(如 blacklist:192.168.1.100),设置 TTL(如 60 秒),自动过期,免去定时清理逻辑。
- 封禁命令示例:
SET blacklist:192.168.1.100 1 EX 60 - 检查是否存在:
EXISTS blacklist:192.168.1.100 - 批量封禁 IP 可用 pipeline,提升吞吐
三、Nginx 配置中嵌入 Lua 拦截逻辑
在 http 或 server 块中定义共享内存池(用于复用 Redis 连接),在 access_by_lua_block 中执行实时判断:
lua_shared_dict redis_pool 10m;
init_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(1000, 1000, 1000)
-- 初始化连接池参数可在此预设
}
access_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeouts(500, 500, 500)
<pre class="brush:php;toolbar:false;">local ip = ngx.var.remote_addr
local key = "blacklist:" .. ip
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.WARN, "redis connect failed: ", err)
return
end
local res, err = red:exists(key)
if res == 1 then
ngx.status = 403
ngx.say("Access denied")
ngx.exit(403)
end
}
注意:生产环境建议使用连接池(redis:pool),避免频繁建连;IP 也可换成 ngx.var.http_x_forwarded_for(需校验可信代理)。
四、封禁操作自动化对接(API 或脚本)
封禁不应手动敲 Redis 命令。提供一个简单 HTTP 接口(例如用 Python Flask)接收封禁请求,写入 Redis:
@app.route('/ban', methods=['POST'])
def ban_ip():
ip = request.form.get('ip')
duration = int(request.form.get('duration', 60))
if not ip or not is_valid_ip(ip):
return {"error": "invalid ip"}, 400
r.setex(f"blacklist:{ip}", duration, "1")
return {"status": "banned", "ip": ip, "expire": duration}
调用示例:curl -X POST http://localhost:5000/ban -d "ip=203.208.60.1&duration=300",5 秒后该 IP 请求即被 Nginx 拦截。











