ThinkPHP 6 的 throttle 中间件需同时满足启用全局中间件、正确路由绑定、准确 IP 获取、适配缓存驱动及避免键冲突才真正生效;推荐自定义中间件实现细粒度限流。

ThinkPHP 6 的 throttle 中间件怎么配才真正生效
默认配置下,throttle 中间件只对当前请求 IP 生效,但如果你没开 app.use_global_middleware 或没在路由分组里显式绑定,它根本不会跑。尤其在 API 路由里,很多人只给控制器加中间件,却忘了路由定义才是执行入口。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认
config/middleware.php中已启用全局中间件开关:'use_global_middleware' => true - 在
app/route/app.php中,用分组方式绑定更可靠:Route::group('api', function () { /* 路由 */ })->middleware('throttle:60,1'); -
throttle:60,1表示每分钟最多 60 次,单位是「分钟」;别写成throttle:60,60——第二个参数是「秒数」,写错会导致限流窗口异常变长 - 注意:该中间件依赖
cache驱动,如果用的是file缓存,在高并发下可能因文件锁导致计数不准;生产环境建议切到redis
IP 获取不准确导致限流失效的常见原因
ThinkPHP 默认用 $request->ip() 取客户端 IP,但在 Nginx + PHP-FPM 架构下,这个值通常是反向代理的内网地址(比如 127.0.0.1),所有请求都算同一个 IP,限流就形同虚设。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 检查 Nginx 配置是否透传真实 IP:
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 在 ThinkPHP 中重写 IP 获取逻辑:在
app/middleware/CheckIp.php里提前设置$request = $request->withHeader('X-Real-IP', $request->header('x-real-ip'));,再交由throttle使用 - 别直接改框架源码里的
Request::ip(),容易被升级覆盖;优先走中间件或继承重写think\Request
throttle 的缓存键名冲突问题
ThinkPHP 的 throttle 中间件默认用 throttle_ + md5($ip . $route) 作缓存键,但如果多个路由共用同一控制器方法(比如 RESTful 的 index 方法同时响应 GET /users 和 GET /posts),它们会共享计数器——这不是 bug,是设计如此,但常被误认为“限流没起作用”。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 需要独立限流时,手动指定唯一标识:
->middleware('throttle:30,1,users')第三个参数就是自定义 key 后缀,避免混用 - key 冲突还可能发生在多级反向代理场景:若 Nginx 和 CDN 都设置了
X-Forwarded-For,$request->ip()可能取到一串逗号分隔的 IP,导致 key 不稳定;务必在中间件里做清洗,只取第一个有效公网 IP - Redis 缓存中可手动查键验证:
redis-cli keys "throttle_*",看是否出现大量相似 key
自定义限流中间件比改配置更可控
官方 throttle 不支持按用户 ID、token 或接口类型差异化限流,硬塞进一个中间件里容易越改越乱。真要细粒度控制,不如自己写一个轻量中间件,逻辑清晰、调试方便、后续也容易加日志或告警。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 新建
app/middleware/ApiRateLimit.php,核心逻辑就三步:取标识 → 查缓存 → 判断并写回 - 标识可以灵活组合:
$key = 'rate_' . md5($request->header('authorization') ?: $request->ip()) . '_' . $request->rule(); - 别用
Cache::inc()直接累加,先get再set,否则并发下可能超限;Redis 推荐用INCR+EXPIRE原子操作 - 返回 429 时,记得加标准头:
$response->header('Retry-After', '60'),让调用方知道等多久
实际部署时最常被忽略的是缓存驱动和 IP 解析链路的耦合性——限流不是加个中间件就完事,它卡在请求入口、代理层、缓存系统三者的交界处,任一环节掉链子,指标就失真。


















