laravel 的 throttle 中间件需显式注册命名策略(如 ratelimiter::for('api', ...))才生效,且默认仅对 api 路由组启用;web 路由中直接使用 throttle:60,1 通常静默失效或报错“rate limiter [api] is not defined”。

直接说结论:Laravel 的 throttle 中间件不是“写上就生效”的开关,它必须配合 RateLimiter::for() 显式注册的命名策略,且只对 api 路由组默认启用——在 web 路由里加 throttle:60,1 通常静默失效。
为什么 throttle:60,1 在 routes/web.php 里不拦请求
常见错误现象:给 GET /test 加了 throttle:60,1,狂刷无反应;或报错 Rate limiter [api] is not defined。
-
throttle中间件本身只是个“壳”,不带内置逻辑,所有规则都来自RateLimiter::for('xxx')注册的策略名 -
throttle:60,1是匿名策略,仅适用于单条路由,但 Laravel 默认的api组中间件配置是throttle:api,它会去找名为api的策略 —— 如果你没在RouteServiceProvider::configureRateLimiting()里定义它,就会报错 -
web路由组默认不启用限流中间件,即使手动加throttle,也依赖缓存驱动(如 Redis);若用array驱动,多进程下计数器完全失效,且不报错
怎样正确定义并使用命名限流策略
核心动作是在 app/Providers/RouteServiceProvider.php 的 configureRateLimiting() 方法里注册策略。
- 策略名必须和中间件参数一致:比如路由写
throttle:api,就必须有RateLimiter::for('api', ...) -
by()决定“谁共享配额”:推荐写法是$request->user()?->id ?: $request->ip(),避免未登录用户触发Call to a member function id() on null - 时间单位只有
perMinute()、perHour()、perDay()—— 没有perSecond(),底层不支持秒级原生限流 - 示例(放在
configureRateLimiting()内):RateLimiter::for('api', function (Request $request) { return Limit::perMinute(100)->by($request->user()?->id ?: $request->ip()); });
API 路由限流为何常不生效?关键在 session 和 key
因为默认的 ThrottleRequests 中间件依赖 session,而 api 路由组默认关闭 StartSession,导致无法识别用户身份,限流退化为按 IP 粗粒度统计,甚至完全失效。
- 不要给 API 路由手动加
StartSession:破坏无状态性,可能引发跨域或并发问题 - 正确做法是重写
resolveRequestSignature(),优先从Authorizationheader 提取 Bearer Token(对应用户),再 fallback 到 IP - 在自定义中间件中覆盖
buildResponse(),返回 JSON 格式的429响应和Retry-After头,否则前端收不到标准错误 -
throttle:60,1,by=ip在 NAT 环境(如校园网、运营商出口)下极易误伤,别用于登录、短信等敏感接口
缓存驱动和 key 设计是限流稳定的关键
限流依赖缓存存储计数器,驱动选错或 key 设计不合理,会导致“漏限”或“误杀”。
- 生产环境必须用
redis或memcached;file或array驱动在多进程/负载均衡下必然失效 - CDN 或 SLB 后所有请求共享一个公网 IP,此时
by=$request->ip()完全不可用,必须换维度:如$request->header('X-Device-ID')、$request->bearerToken(),且需确保值是纯 ASCII 字符串,不含空格、斜杠、冒号 - key 冲突很隐蔽:比如用了
by=auth:id,但 token 过期后用户反复重登,旧 session 计数器仍占坑,表现为“一人触发、全员受限” - 调试时可用
php artisan tinker手动查缓存:Cache::get('throttle|123.45.67.89')(key 格式取决于你用的缓存驱动)
真正难的不是写几行 RateLimiter::for(),而是确认缓存驱动是否真实生效、key 是否在所有部署环境下保持唯一、以及当大量 429 出现时,到底是策略太严,还是 key 太宽泛导致误伤——这些细节往往藏在日志和缓存键里,而不是代码行中。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











