CodeIgniter 限流需分层选择:CI4用Redis驱动Throttler、CI3用RestServer按场景选rest_limits_method、Nginx适合静态IP封禁;关键要配对Retry-After头与真实剩余时间。

CodeIgniter 没有开箱即用的全局 IP 限流功能,但能通过 Throttler(CI4)、RestServer 扩展(CI3)或 Web 服务器层(如 Nginx)三类方式稳定落地——关键看你要控的是「整个站」还是「某个接口」,以及是否已有 Redis 环境。
CI4 的 Throttler 类为什么总不生效
最常见现象是 $throttler->check() 始终返回 true,或者并发一上来就失效。根本不在代码逻辑,而在缓存驱动配置:
-
app/Config/Cache.php中默认的file驱动在高并发下会因文件锁竞争导致计数跳变甚至静默失败 - 必须显式启用
redis驱动:把'default' => 'redis'写进$handlers,并确认Redis服务可连通 -
check()第一个参数不能是空字符串或null,建议统一用$request->getIPAddress() - 时间单位必须是整数秒,
MINUTE是常量(值为60),别写成字符串"60"或变量未定义
CI3 用 RestServer 实现限流,rest_limits_method 怎么选
RestServer 的 $config['rest_limits_method'] 支持四个取值,每种适用场景和坑点都很具体:
-
IP_ADDRESS:适合未登录用户或公开接口,但 NAT 环境(如企业出口 IP)会导致整批用户被误封 -
API_KEY:需开启$config['rest_enable_keys'] = true并维护keys表,适合已授权调用方,粒度细、可审计 -
METHOD_NAME:按控制器方法名(如users_get)限流,不区分调用者,适合统一压测某类操作 -
ROUTED_URL:精确到路径 + 方法(如GET /api/v1/users),但遇到带参数路由(/users/{id})会为每个 ID 生成独立 key,limits表可能指数膨胀,慎用于动态路径
限流响应里缺 Retry-After 头,前端根本不知道等多久
RestServer 和 CI4 Throttler 都不会自动设置这个头,而前端依赖它做退避重试。硬编码值风险大,推荐读取实际剩余秒数:
- CI4 过滤器中,改用
$throttler->hit($ip, 10, MINUTE)获取返回数组,其中['available_in']是剩余等待秒数 - 然后手动加头:
Services::response()->setHeader('Retry-After', (string) $result['available_in']) - RestServer 则需在控制器里拦截
$this->response,用$this->response->setHeader('Retry-After', ...)补上
Nginx 层直接封 IP,比 PHP 层更早也更省资源
如果目标只是禁止特定 IP 访问管理后台或敏感路径(比如 /admin),Nginx 是更轻量、更可靠的方案:
- 在 server 块或 location 块里写
deny 192.168.1.100;或allow 10.0.0.0/8; deny all; - 注意反向代理场景下,要先配
real_ip_header X-Forwarded-For;和set_real_ip_from,否则拿到的是代理 IP - 封禁规则必须放在
rewrite之前,否则请求还没走到限流逻辑就被重写了 - 这种封禁不经过 PHP,对服务器压力最小,但无法做“每分钟最多 10 次”这类动态判断
真正难的不是写几行限流代码,而是决定在哪一层做、用什么存储、怎么把 Retry-After 和真实剩余时间对齐——这些细节一旦漏掉,限流就只剩个 HTTP 状态码,起不到防护作用。


















