throttlerequests中间件需正确配置才能生效:必须加入对应中间件组(如api路由需手动添加)、使用throttle:60,1格式参数、确保redis缓存驱动启用,并自定义resolverequestsignature()实现精准限流。

ThrottleRequests 中间件怎么配才真正生效
默认的 ThrottleRequests 中间件只对「已认证用户」或「IP 地址」限流,但很多人配完发现没效果——根本原因是没把它加到正确的中间件组里,或者路由没走这个中间件。
常见错误现象:Too Many Requests (429) 从不出现,哪怕狂刷接口;或者只对登录用户限流,游客完全不受控。
- 必须确保路由用了
throttle:60,1这类中间件别名,或显式调用app('Illuminate\Routing\Middleware\ThrottleRequests') - 如果用的是 API 路由(
routes/api.php),默认中间件组不含throttle,得手动加:在app/Http/Kernel.php的$middlewareGroups['api']里插入\Illuminate\Routing\Middleware\ThrottleRequests::class - 参数格式是
throttle:60,1(60 次/1 分钟),不是throttle:60/1或throttle:60,60—— 第二个参数单位是「分钟」,不是秒
Laravel 9+ 的速率限制键(key)怎么自定义
默认按 IP 限流太粗暴:NAT 环境下一群用户共用一个 IP,容易误杀;想按用户 ID、API token 或请求头区分?得重写 resolveRequestSignature() 方法。
使用场景:后台管理接口想对每个管理员独立限流,或 SaaS 多租户系统按 X-Tenant-ID 区分额度。
- 新建中间件继承
ThrottleRequests,覆盖resolveRequestSignature() - 返回值必须是字符串且具备唯一性,比如:
return $request->user()?->id ?: $request->ip().'_'.$request->header('User-Agent') - 注意不要包含敏感信息(如明文 token),也不要带斜杠或控制字符,否则 Redis 键名会出错
- 性能影响:每次请求都执行该方法,避免在其中做 DB 查询或远程调用
Redis 配置不对会导致限流完全失效
限流依赖 Laravel 的缓存驱动存储计数器,默认用 cache 驱动,但如果你改过 CACHE_DRIVER=file 或 array,限流就退化成「单进程有效」——多 worker、多机器时彻底失效。
错误现象:本地开发(file 缓存)一切正常,上线后 429 完全不触发;或部分服务器报 ERR max number of clients reached。
- 生产环境必须用
redis或memcached,在.env设CACHE_DRIVER=redis,并确认REDIS_HOST可达 - 检查 Redis 连接池配置:Laravel 默认用
predis,若换phpredis,需确保扩展已装且redis.session_locking_enabled不干扰 - 如果用 Laravel Octane,注意
throttle中间件在 Swoole 模式下仍走 Redis,但自定义 key 逻辑不能依赖$request->session()(无 session)
测试限流是否生效的可靠方式
别靠浏览器 F5 刷,也别用 Postman 点几次——HTTP 工具默认复用连接、带缓存头,结果不可信。真实压测必须模拟并发、隔离 IP 和 User-Agent。
推荐做法:
- 用
curl -H "User-Agent: test-$(date +%s)" http://your.app/api/test循环发请求,避免被当成同一来源 - 写个简单脚本跑 10 个并发:用
ab -n 100 -c 10 http://your.app/api/test(Apache Bench),观察返回状态码分布 - 直接查 Redis:运行
redis-cli keys "*throttle*",看有没有类似throttle|127.0.0.1|GET|/api/test的 key,再hgetall看剩余次数 - 注意:Laravel 10+ 把 key 前缀从
throttle:改为throttle|,旧版监控脚本要同步更新
最容易被忽略的点:中间件顺序。如果 ThrottleRequests 在 EncryptCookies 或 StartSession 之后,而你又依赖 session 数据生成 key,那在 API 路由里它根本拿不到 session——因为 API 默认无 session 中间件。











