必须用OpenResty,因其内置LuaJIT和lua-resty-redis,支持在access阶段原子化执行Redis限流逻辑;原生Nginx无内嵌Lua、不支持直连Redis,第三方模块功能弱且不稳定。

直接在 Nginx 层防 CC 攻击,单机限流(如 limit_req)完全不够用——攻击流量会绕过单点,打垮任意一台实例。真正有效的方案是:让所有 Nginx 实例共享同一套限流状态,靠 Redis 做中心计数器,再用 Lua 脚本保证每次判断+更新的原子性。这必须用 OpenResty,原生 Nginx 不支持。
为什么必须用 OpenResty 而不是原生 Nginx
原生 Nginx 没有内嵌 Lua 解释器,也不能直连 Redis。你无法在请求进入阶段(access 阶段)执行逻辑判断并实时查 Redis。第三方模块(如 HttpRedis2Module)功能弱、不维护、不支持复杂脚本,容易出错。OpenResty 集成了 LuaJIT 和 lua-resty-redis,开箱即可在 access_by_lua_file 中安全调用 Redis,这是防 CC 的基础前提。
关键配置:Nginx + Lua + Redis 三步联动
在 server 或 location 块中启用限流入口:
- 使用
access_by_lua_file /path/to/rate_limit.lua;,确保在代理转发前拦截请求 - 不要用
content_by_lua_*,那时后端已处理,限流失去意义 -
worker_processes auto;,每个 worker 独立建 Redis 连接池,避免争抢 - 在 Lua 脚本里用
resty.redis或更推荐的resty.limit.count封装好的限流器
限流逻辑:基于 IP 的滑动窗口或令牌桶
CC 攻击常表现为短时间大量相同 IP 请求,所以 key 推荐用 "cc:ip:" .. ngx.var.binary_remote_addr。Redis 脚本需完成四件事原子执行:
- 读取该 IP 当前计数和上一次访问时间
- 根据时间差补发令牌(令牌桶)或截取最近 N 秒窗口(滑动窗口)
- 判断是否超限:比如“10 秒内最多 30 次”
- 更新 Redis 中的计数/时间戳,并设 TTL(如
EXPIRE key 10)
返回 1 则放行,返回 0 则在 Lua 中调用 ngx.exit(429) 直接拒绝,不发往后端。
增强防护:结合简单缓存与响应头标记
对已确认为恶意的 IP,可额外写入 Redis 黑名单(如 blacklist:ip:xxx),TTL 设为 5–30 分钟,在 Lua 中优先检查黑名单,命中即 403;同时给正常响应加 X-RateLimit-Remaining 头,便于前端或监控识别当前余量。缓存本身不用于防 CC(它缓解后端压力,但不阻止请求到达 Nginx),但配合限流能减少误伤下的资源浪费。


















