ThinkPHP 6 的 Throttle 限流需配置、绑定、缓存驱动、IP 获取四环严丝合缝;缺一即失效,须正确配置 key、visit_rate,启用 Redis 缓存并显式绑定中间件。

ThinkPHP 6 中用 Throttle 中间件做访问频率限制,核心不是“装了包就自动生效”,而是配置、绑定、缓存驱动、IP 获取这四环必须严丝合缝。漏掉任意一环,限流就形同虚设。
中间件注册与路由绑定要双保险
只在 app/middleware.php 里加一行类名,不代表所有路由都受控——它只是声明可用;真正起作用,得靠路由层显式调用。
- 全局启用:确保
config/app.php中'use_global_middleware' => true已开启 - 分组绑定更稳妥:API 路由建议统一用分组方式,比如
Route::group('api', function () { /* 子路由 */ })->middleware('throttle:60,1'); - 单接口精细控制:直接在路由定义末尾加
->middleware(\think\middleware\Throttle::class, ['visit_rate' => '10/m', 'key' => '__IP__']) - 常见失效原因:装完包没运行
php think vendor:publish导致config/throttle.php缺失,或路由里漏写->middleware()
key 配置决定限流粒度,别迷信默认值
key 参数不是可有可无的开关,它直接定义“谁和谁共用一个计数器”。填错等于限错了对象。
-
'key' => '__IP__':按客户端真实 IP 限流(需先解决代理 IP 问题) -
'key' => '__CONTROLLER__/__ACTION__':按控制器+方法限流,适合通用接口 -
'key' => function($throttle, $request) { return $request->ip() . '_' . ($request->post('scene') ?: 'default'); }:上传类接口推荐,把业务维度(如头像/封面)也纳入键值 - 避免写
'key' => true:它等价于只取$_SERVER['REMOTE_ADDR'],在反向代理下必然不准 - 闭包路由中
$request->rule()可能为空,建议 fallback 到$request->path()
visit_rate 单位必须规范,时间窗口是硬重置
visit_rate 不是数学表达式,是框架硬解析的字符串,格式错误会静默退化为不限流。
立即学习“PHP免费学习笔记(深入)”;
- 只认三种后缀:
/m(分钟)、/h(小时)、/d(天),不能写/s或/15s - 要实现“15 秒最多 3 次”,换算成
'12/m'(3 × 60 ÷ 15),而非'3/15s' - 时间窗口是固定窗口(Fixed Window),比如
'10/m'指从每分钟第 0 秒开始计,不是滑动统计过去 60 秒 - 值为
null表示不限,别写成字符串'null',否则报错
生产环境必须切 Redis,文件缓存高并发下不可靠
限流本质是原子计数,而文件缓存的 Cache::inc() 在底层仍是 get + set,存在竞态风险。
- 确认
config/cache.php中'default' => 'redis' - 检查
config/redis.php的host、port、database是否正确 - 若用低版本 Redis 驱动,注意
set($key, $val, 0)可能被忽略导致 key 永不过期,建议显式设 TTL 如3600 - 上传接口尤其要切 Redis:POST 请求本身不长,但高频发起才是风险点,文件缓存根本扛不住



















