Nginx + Lua 实现每日访问次数限制与防盗链校验
🧾 Nginx + Lua 实现每日访问次数限制与防盗链校验(以 /cmap 图片接口为例)
一、应用场景
/cmap 是一个图片接口(通过 proxy_pass 转发到后端),
需要实现:
- 每日最多访问 1000 次
- 防盗链检查(仅允许特定来源 Referer)
- 当返回 403 / 429 时输出 JSON 格式提示
- 正常访问时返回图片内容(不影响 Content-Type)
二、依赖模块
要启用 access_by_lua_block,Nginx 需要编译 ngx_http_lua_module 模块。
如果是自己构建镜像,请确保编译参数中包含:
--add-module=/path/to/lua-nginx-module
并在 Nginx 配置文件中引入 Lua 共享内存:
lua_shared_dict access_limit 10m;
三、核心配置示例
location /cmap {
proxy_pass http://10.18.55.96/cmap/layer;
access_by_lua_block {
-- 每日访问上限
local limit = 1000
local key = "cmap_access_count"
local dict = ngx.shared.access_limit
local day = os.date("%Y%m%d")
local count_key = key .. ":" .. day
-- ======================
-- 防盗链检查
-- ======================
local headers = ngx.req.get_headers()
local referer = headers["referer"]
-- 允许的来源列表(使用正则匹配)
local allowed_domains = {
"^https?://10%.18%.55%.98:30001/",
"^https?://yourdomain%.com/"
}
-- 是否允许空 Referer(例如浏览器直接访问)
local allow_empty_referer = true
local valid = false
if referer then
for _, pattern in ipairs(allowed_domains) do
if referer:match(pattern) then
valid = true
break
end
end
elseif allow_empty_referer then
valid = true
end
if not valid then
ngx.status = 403
ngx.header.content_type = "application/json; charset=utf-8"
ngx.say('{"code":403,"message":"Forbidden: Invalid Referer"}')
return ngx.exit(403)
end
-- ======================
-- 每日访问次数统计
-- ======================
local current = dict:get(count_key)
if not current then
dict:set(count_key, 0, 86400) -- 有效期1天
current = 0
end
if current >= limit then
ngx.status = 429
ngx.header.content_type = "application/json; charset=utf-8"
ngx.say(string.format(
'{"code":429,"message":"Daily access limit exceeded","count":%d,"limit":%d}',
current, limit
))
return ngx.exit(429)
end
local new_count = dict:incr(count_key, 1)
if not new_count then
dict:set(count_key, current + 1, 86400)
new_count = current + 1
end
ngx.log(ngx.INFO, string.format("cmap accessed %d/%d times today", new_count, limit))
}
}
四、逻辑说明
| 功能 | 说明 |
|---|---|
| 防盗链 | 校验 Referer 是否来自白名单域名或允许为空 |
| 访问计数 | 使用 ngx.shared.DICT 记录每日访问次数 |
| 时间粒度 | 以当天日期为 key(如 20251031) |
| 响应格式 | 正常请求 → 返回图片;403 / 429 → 返回 JSON |
| 过期机制 | 每天自动重置计数(缓存过期 86400 秒) |
五、Referer 匹配规则
| 写法 | 匹配示例 | 说明 |
|---|---|---|
"http://10.18.55.98:30001/" |
仅匹配该路径开头(string.find) |
简单匹配 |
"^https?://10%.18%.55%.98:30001/" |
匹配 http 或 https 前缀 | 推荐正则匹配 |
"^https?://yourdomain%.com/" |
匹配自定义域名 | 正则匹配更安全 |
六、其他建议
- 若服务器被直接访问(无 Referer),可启用
allow_empty_referer = true - 若需更精细限制(如按 IP 统计、按小时限流),可在 key 中加上
ngx.var.remote_addr或os.date("%H") - 若使用 Docker 运行,需要在镜像中包含
lua-nginx-module和luajit

浙公网安备 33010602011771号