thinkphp 6 的 throttle 中间件默认不生效,因其仅对显式绑定控制器方法的路由生效,且依赖正确配置 redis 缓存、x-real-ip 头透传及全局或路由级中间件启用。

ThinkPHP 6 的 throttle 中间件默认不生效——不是你配错了,是它根本没跑起来。核心原因就三个:路由没绑定控制器、IP 拿的是 127.0.0.1、缓存驱动还在用 file。
throttle 中间件为什么加了也不限流
常见现象是加了 ->middleware('throttle:60,1'),但接口照刷不误,Access-Control-Allow-Origin 都返回了,就是不限制。
- 只对「显式绑定控制器方法」的路由生效,比如
Route::get('api/user', 'Api/UserController@index');闭包路由Route::get('api/user', function(){})、资源路由未显式绑定、Route::any+ 闭包,全都不触发 - 中间件必须在路由分组或全局启用:确认
config/middleware.php中'use_global_middleware' => true已开启;更稳妥的做法是在app/route/app.php里用分组绑定:Route::group('api', function () { /* 路由 */ })->middleware('throttle:60,1') -
throttle:60,1第二个参数是「秒数」,不是分钟。写成throttle:60,60就变成每小时 60 次,窗口拉得太长,看起来像没限
真实 IP 怎么取才不会全算成 127.0.0.1
Nginx + PHP-FPM 架构下,$request->ip() 默认返回反向代理地址,所有请求都挤在一个计数器里,限流形同虚设。
- Nginx 必须透传真实 IP:
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - PHP 层只信任
X-Real-IP,别无脑用X-Forwarded-For(可伪造);可在中间件里提前标准化:$request = $request->withHeader('X-Real-IP', $request->header('x-real-ip')); - 如果用了多级代理(比如 CDN + Nginx),
X-Forwarded-For可能是一串逗号分隔的 IP,得清洗:只取第一个非私有地址(排除127.0.0.1、10.0.0.0/8等) - 框架的
trusted_proxies配置必须匹配你的代理 IP 段,否则Request::ip()不会自动解析X-Forwarded-For
缓存键冲突和 Redis 原子性问题
限流失效的隐蔽原因:缓存 key 冲突、并发下计数超限、过期没设上。
-
throttle默认 key 是throttle_+md5($ip . $route);RESTful 路由如GET /users和GET /posts若共用同一个index方法,就会共享计数器。需要独立限流时,手动加第三个参数:->middleware('throttle:30,1,users') - 别用
$redis->incr($key); $redis->expire($key, 60)—— 这两步非原子,高并发下可能 incr 成功但 expire 失败,key 永不过期,后续所有请求都被拦住 - 必须用 Lua 脚本保证原子性:
local current = redis.call('incr', KEYS[1]) if current == 1 then redis.call('expire', KEYS[1], ARGV[1]) end return current,然后$redis->eval($lua, [$key], 1, 60) - 缓存驱动不能用
file:文件锁在并发下会导致计数不准;生产环境必须切到redis或memcached
自定义限流中间件比 throttle 更可控
内置 throttle 灵活性差,比如没法按用户 token 限流、没法统一返回 Retry-After 头、异常时静默失败。
- 限流逻辑必须放在中间件层,绝不能写在控制器里——否则跨路由、跨方法无法聚合统计,也拦不住恶意刷量
- Redis key 命名要带命名空间和维度,例如
rate:api:v1.users.index:192.168.1.100或rate:api:token_user_id:abc123,避免混用 - 429 响应必须手动加
Retry-After头,前端才好做退避;ThinkPHP 异常处理器里要显式设置:$response->header('Retry-After', 60) - catch 块里也要返回响应,否则 Redis 报错时中间件静默失败,限流直接失效
真正卡住限流落地的,从来不是算法选令牌桶还是滑动窗口,而是 IP 怎么取、key 怎么命、Redis 操作怎么原子、中间件在哪一层注册——这些地方错一个,整个限流就等于没开。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











