Laravel 8.x 的 throttle 中间件需按三类参数正确配置:匿名策略(如 throttle:60,1)、命名策略(如 throttle:api,须在 RouteServiceProvider 中定义)、内置 key 生成器(如 throttle:60,1,users);缓存驱动必须为 Redis/Memcached,且 web 路由慎用。

Laravel 8.x 的 throttle 中间件不是“写上就生效”的黑盒,它的参数行为取决于是否使用命名策略,以及缓存驱动和路由分组是否匹配。直接写 throttle:60,1 能工作,但容易踩坑——尤其在 web 路由里失效、或报 Rate limiter [api] is not defined 错误时。
下面分三类讲清楚怎么设参数才真正起作用:
throttle中间件的三种参数写法及适用场景
throttle:60,1
表示「每分钟最多 60 次」,单位是分钟(支持小数,如0.5= 30 秒)。这是匿名策略,适用于单个路由临时限流,不依赖命名配置。
✅ 可直接用于routes/api.php或routes/web.php的某条路由
❌ 不推荐用于web组大量路由(因 session/cookie 影响 key 生成,且默认没配 Redis 易静默失效)throttle:api
表示使用名为api的命名限流策略。它本身不带数字,所有逻辑来自RouteServiceProvider::configureRateLimiting()中定义的RateLimiter::for('api', ...)。
✅ 必须配合RateLimiter::for('api', fn($req) => Limit::perMinute(100)->by(...))使用
❌ 如果没在configureRateLimiting()里定义'api',就会报错:“Rate limiter [api] is not defined”throttle:60,1,users
第三个参数users是 Laravel 内置 key 生成器:登录用户用Auth::id(),未登录则 fallback 到 IP。
✅ 省去自定义闭包,适合简单按用户/游客区分的场景
❌ 不能做角色差异化(比如 VIP 用户放宽到 200 次),需改用命名策略 + 条件判断
命名策略必须在 RouteServiceProvider 中定义
光写 throttle:api 不会自动生效。你得打开 app/Providers/RouteServiceProvider.php,在 configureRateLimiting() 方法里注册:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
protected function configureRateLimiting(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(100)->by($request->user()?->id ?: $request->ip());
});
RateLimiter::for('vip', function (Request $request) {
$user = $request->user();
if ($user && $user->isVip()) {
return Limit::perMinute(500)->by($user->id);
}
return Limit::perMinute(100)->by($user?->id ?: $request->ip());
});
}
然后路由就可以这样用:
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware('throttle:vip');
关键配置检查清单(避免静默失败)
- 缓存驱动必须支持原子操作:Redis 或 Memcached。
file或array驱动会导致限流完全失效(不报错,但每次请求都重置计数) -
web路由组默认不启用throttle,即使你手动加了中间件,也建议改用api组(自带throttle:api配置) -
by()的值要稳定:同一用户不同请求必须生成相同 key,否则会被算成多个桶。别用$request->user()->id(未登录会报错),要用$request->user()?->id - 时间单位只有
perMinute()、perHour()、perDay()—— 没有perSecond(),底层不支持秒级原生限流
不复杂但容易忽略











