OpenResty封禁高频404访问IP方法
|
freeflydom
2026年3月10日 15:46
本文热度 57
|
:OpenResty封禁高频404访问IP方法针对 OpenResty 环境下需要封禁大量产生 404 请求的 IP 的需求,可以利用 OpenResty 的 Lua 能力实现动态、实时的 IP 封禁策略。下面介绍主流方法,并给出具体实现示例。
基于 log_by_lua 统计并动态封禁(推荐)
在请求结束时(log_by_lua 阶段)统计每个 IP 产生 404 的次数,当超过预设阈值时将该 IP 加入黑名单(存储在共享内存中),并在后续请求的 access_by_lua 阶段进行拦截。
配置步骤
定义共享内存(用于存储黑名单和临时计数器)
在 http 块中声明两个 lua_shared_dict:
http {
lua_shared_dict ip_blacklist 10m;
lua_shared_dict ip_404_count 10m;
...}在 access 阶段检查黑名单
在需要保护的 server 或 location 中添加:
server {
listen 80;
server_name example.com;
access_by_lua_block {
local blacklist = ngx.shared.ip_blacklist
local ip = ngx.var.remote_addr
if blacklist:get(ip) then
ngx.exit(ngx.HTTP_FORBIDDEN) -- 返回 403
end }
...}在 log 阶段统计 404 并触发封禁
在同一个 server 或 location 中添加:
log_by_lua_block {
local status = ngx.status
if status ~= 404 then
return -- 只统计 404
end
local ip = ngx.var.remote_addr
local count_dict = ngx.shared.ip_404_count
local blacklist = ngx.shared.ip_blacklist
-- 统计时间窗口(例如 60 秒)
local window = 60
local threshold = 10 -- 60 秒内 10 次 404 则封禁
local ban_time = 600 -- 封禁 600 秒
-- 原子增加计数,并设置过期时间
local new_count, err = count_dict:incr(ip, 1, 0, window)
if not new_count then
ngx.log(ngx.ERR, "failed to incr 404 count: ", err)
return
end
-- 如果达到阈值,加入黑名单
if new_count >= threshold then
local ok, err = blacklist:set(ip, true, ban_time)
if not ok then
ngx.log(ngx.ERR, "failed to set blacklist: ", err)
else
-- 可选:删除计数器,避免重复封禁
count_dict:delete(ip)
end
end}完整配置示例
http {
lua_shared_dict ip_blacklist 10m;
lua_shared_dict ip_404_count 10m;
server {
listen 80;
server_name clicksun.com.cn;
access_by_lua_block {
local blacklist = ngx.shared.ip_blacklist
if blacklist:get(ngx.var.remote_addr) then
ngx.exit(ngx.HTTP_FORBIDDEN)
end }
location / {
try_files $uri $uri/ =404;
}
log_by_lua_block {
if ngx.status ~= 404 then return end
local ip = ngx.var.remote_addr
local count_dict = ngx.shared.ip_404_count
local blacklist = ngx.shared.ip_blacklist
local window = 60
local threshold = 10
local ban_time = 600
local new_count, err = count_dict:incr(ip, 1, 0, window)
if not new_count then
ngx.log(ngx.ERR, "incr failed: ", err)
return
end
if new_count >= threshold then
blacklist:set(ip, true, ban_time)
count_dict:delete(ip) -- 可选
end }
}}
注意事项
阈值与时间窗口:根据实际流量调整 window(统计周期)和 threshold(阈值),避免误封正常用户。
原子操作:incr 方法自带原子性,适合高并发场景。
内存管理:共享字典设置了大小,过期 IP 会自动清理。
日志级别:可在 error_log 中观察封禁情况。
分布式环境:如果有多台 OpenResty 节点,建议使用 Redis 等外部存储统一黑名单。
该文章在 2026/3/10 15:46:32 编辑过