throttlerequests中间件默认启用但仅对登录注册等内置路由生效,自定义路由需手动添加;须确认kernel.php中已注册且缓存驱动为redis、认证中间件前置以确保按用户id限流。

ThrottleRequests 中间件怎么启用
默认就开着,但只对登录、注册等内置路由生效。想给自定义路由限流,得手动加中间件。
常见错误是直接在 routes/web.php 里写 ->middleware('throttle:60,1') 却没确认该中间件是否已注册——Laravel 从 5.2 起已预装,但如果你删过 app/Http/Kernel.php 里的 ThrottleRequests::class,就会静默失效。
- 检查
app/Http/Kernel.php的$middlewareGroups['web']或$routeMiddleware里有没有'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class - 在路由分组中使用:
Route::middleware('throttle:10,1')->group(...),其中10是次数,1是分钟(单位固定为分钟) - 不要在 API 路由里误用 web 组的 throttle;API 应走
api中间件组,并确保throttle已映射到该组
怎么按用户 ID 或 IP 区分限流粒度
默认 throttle 基于 IP,但登录用户应按 user.id 限流,否则共享 IP 的用户会互相干扰。
关键在 ThrottleRequests 构造逻辑:它调用 resolveRequestSignature() 获取“签名”,这个签名决定谁和谁算同一组。默认实现优先取 $request->user()?->id,没登录才 fallback 到 IP。
- 确保路由绑定了
auth中间件,否则$request->user()为 null,退化成 IP 限流 - 如果用 API Token 认证(如 Sanctum),需确认
auth:sanctum正确执行,否则仍按 IP 算 - 自定义签名?重写中间件或继承
ThrottleRequests,但多数情况只需保证认证中间件前置即可
缓存驱动影响限流行为
限流计数器存在缓存里,用的是 Laravel 默认缓存驱动。如果用的是 file 或 array 驱动,多机部署时完全不生效——每台机器各自计数。
错误现象:本地开发一切正常,上线后限流形同虚设;或者 Nginx 后面挂了多个 PHP-FPM 实例,用户刷几次就解封。
- 生产必须用支持原子操作的缓存:推荐
redis(cache.driver=redis),memcached次之 - Redis 要求版本 ≥ 2.6.12(因依赖
INCR+EXPIRE原子组合),低版本可能漏计数 - 别在
.env里配CACHE_DRIVER=array上生产,这个驱动连跨请求都不共享,仅适合测试
自定义响应格式和状态码
触发限流时默认返回 429 状态码和纯文本 Too Many Requests,前端难处理。要改响应,不能改中间件源码,得通过异常渲染拦截。
Laravel 把 ThrottleRequests 抛出的异常统一转为 ThrottleRequestsException,最终由 App\Exceptions\Handler::render() 处理。
- 在
app/Exceptions/Handler.php的render()方法里加判断:if ($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) - 返回 JSON 响应:
return response()->json(['message' => '请求太频繁,请稍后再试'], 429) - 注意:别漏掉
use Illuminate\Http\Exceptions\ThrottleRequestsException;,否则类名解析失败,异常直接透出
真正容易被忽略的是缓存驱动和认证中间件顺序——这两个点不出错时一切丝滑,一出错就是线上限流完全失效,且日志里几乎不报错。










