ThinkPHP 6 上传接口限流需用 topthink/think-throttle v2+,关键配置包括:visit_method 设为 ['POST'],key 自定义以区分上传类型,缓存驱动切 Redis,配合 Nginx limit_req 做前置防护,并避免在中间件中操作 $_FILES。

用 think-think/think-throttle 中间件最直接
ThinkPHP 6 原生不带上传接口限流能力,但官方生态里 topthink/think-throttle 是目前最稳定、适配度最高的方案——它基于缓存计数 + 时间窗口,不依赖数据库,对上传类接口(如 POST /api/upload)天然友好。
常见错误是只给路由加中间件,却没关掉 visit_method 的默认白名单:它默认只拦截 GET,而上传几乎全是 POST。漏配会导致“明明配了限流,但狂刷上传一点反应都没有”。
- 安装命令必须带版本约束:
composer require topthink/think-throttle:^2.0(v2+ 才支持 TP6.1+ 的 Request 对象注入) - 在
config/throttle.php中显式声明:'visit_method' => ['POST'] - 上传接口路由建议单独配置,避免和普通 API 混用同一限流策略,例如:
->middleware(\think\middleware\Throttle::class, ['visit_rate' => '5/m', 'key' => '__IP__'])
key 配置决定限流粒度,别用默认 true
很多人抄文档写 'key' => true,结果发现“同一个 IP 上传不同文件也被算作同一次”,这是因为 true 等价于只取 REMOTE_ADDR,完全忽略请求体差异——而上传接口的关键区分点往往在字段名(如 file vs avatar)或业务参数(如 scene=avatar)。
真实场景中,你通常要限制的是“单 IP 每分钟最多上传 3 次头像”,而不是“单 IP 每分钟最多发 3 次 POST”。所以 key 必须自定义:
立即学习“PHP免费学习笔记(深入)”;
- 按上传类型区分:
'key' => function($throttle, $request) { return $request->ip() . '_' . ($request->post('scene') ?: 'default'); } - 防绕过:加签名防篡改,比如把
md5($request->ip() . $request->post('filename'))当 key(注意 filename 可伪造,需服务端校验) - 别用
__CONTROLLER__/__ACTION__:TP6 的上传路由常是闭包或资源路由,这类占位符会解析失败,返回空字符串,导致所有请求共用一个计数器
上传大文件时,visit_rate 单位选错会失效
限流不是卡住连接,而是“拒绝新请求”。如果用户上传一个 200MB 文件,耗时 90 秒,而你配了 'visit_rate' => '5/m',那这 90 秒内他其实还能再发 4 次——因为计数器每 60 秒重置,且请求进入中间件时才开始判断,不等上传完成。
真正要防的是“高频发起上传请求”,不是“单次上传时长”。所以单位优先选 /m 或 /h,慎用 /s(除非做秒级风控);更关键的是配合 Nginx 层做前置防护:
- Nginx 配置
limit_req zone=upload burst=3 nodelay;,控制连接建立频率 - PHP 层限流只做兜底,重点验证
$_FILES是否真实存在、大小是否超upload_max_filesize - 别在限流中间件里读
$_FILES或调move_uploaded_file():TP6 的中间件执行时机早于文件临时存储完成,此时$_FILES可能为空或不完整
缓存驱动影响并发准确性,Redis 比 file 更可靠
默认缓存驱动是 file,在多 Worker(如 Swoole 或 PHP-FPM 多进程)下会出现计数偏差:两个进程同时读到 “当前 4 次”,各自+1 写回,结果变成 5 而不是预期的 6。小流量看不出来,压测时频次会明显高于配置值。
生产环境务必切 Redis:
- 确认
config/cache.php中'default' => 'redis'已启用 -
throttle.php中的'prefix'建议带上应用标识,比如'prefix' => 'tp6_upload_throttle_',避免和其他服务冲突 - Redis 连接异常时,
think-throttle默认静默降级为不限流——这点很危险,建议在visit_fail_response里加日志:Log::warning('throttle cache fail, fallback to no limit')
上传限流真正的难点不在配参数,而在厘清“谁该被限”(IP?用户?设备指纹?)、“按什么维度限”(总次数?单文件类型?带宽占用?),以及缓存、反代、CDN 多层之间如何协同。配完记得用 ab -n 10 -c 5 http://xxx.com/api/upload 实测,别只信配置文件里的数字。


















