Laravel生产环境API限流必须使用Redis缓存、正确识别真实客户端IP、为v1/v2版本分别配置独立限流策略,并自定义429 JSON响应;否则将出现限流失效或误触发。

在Laravel生产环境中,API限流必须确保跨进程、跨服务器请求计数一致,否则会出现“明明配置了每分钟60次,却频繁429”或“完全不触发限流”的故障;这要求缓存驱动必须为Redis、key构造需兼容真实客户端IP、中间件绑定不能依赖session机制。
确认并切换至Redis缓存驱动
打开 config/cache.php,检查 default 键值是否为 redis;若为 file 或 array,限流在多Worker下必然失效——因为这两个驱动无法原子递增、不支持TTL、不同PHP进程间数据完全隔离。
执行 php artisan config:clear 清除配置缓存,再运行 php artisan cache:clear 清空当前缓存,避免旧缓存残留干扰。
验证Redis连接:在 tinker 中执行 Cache::store('redis')->put('test', 'ok', 10),无报错且能读出值即表示Redis已就绪。
修复真实客户端IP识别逻辑
默认 $request->ip() 在Nginx反代或CDN后返回的是内网IP(如127.0.0.1),必须改用可信代理头提取真实IP。
在 app/Http/Middleware/TrustProxies.php 中,将 $proxies 设为实际负载均衡器IP段,例如:protected $proxies = ['127.0.0.1', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
然后在限流策略中不再直接调用 $request->ip(),改用:$request->header('X-Forwarded-For') ?: $request->ip();Cloudflare用户请换为 $request->header('Cf-Connecting-Ip'),阿里云SLB请换为 $request->header('X-Real-IP')。
注册v1/v2双版本独立限流策略
打开 app/Providers/RouteServiceProvider.php,定位到 configureRateLimiting() 方法内部。
粘贴以下代码,为v1和v2接口分别注册策略:
复制AI写代码
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
RateLimiter::for('api-v1', function (Request $request) {
return Limit::perMinute(300)->by($request->user()?->id ?? ($request->header('X-Forwarded-For') ?: $request->ip()));
});
RateLimiter::for('api-v2', function (Request $request) {
$token = $request->bearerToken();
if ($token && preg_match('/^[a-zA-Z0-9\-\_\.]+$/i', $token)) {
return Limit::perMinute(60)->by(hash_hmac('sha256', $token, config('app.key')));
}
return Limit::perMinute(5)->by($request->header('X-Forwarded-For') ?: $request->ip());
});
【注意】v2策略中未携带有效token时仅允许5次/分钟,这是防暴力探测的关键兜底。
路由分组显式绑定命名中间件
进入 routes/api.php,找到v1路由分组,确保其使用 throttle:api-v1 而非硬编码 throttle:300,1:
Route::prefix('v1')->middleware(['api', 'throttle:api-v1'])->group(function () { ... });
同理,v2路由组必须绑定 throttle:api-v2:
Route::prefix('v2')->middleware(['api', 'throttle:api-v2'])->group(function () { ... });
禁止将 throttle:api-v1 写在 app/Http/Kernel.php 的 $middlewareGroups['api'] 中——这会导致所有API路由(包括v2)被统一限流,彻底破坏v2的严格控制意图。
自定义429响应为JSON格式
在 app/Http/Middleware/ApiThrottleRequests.php 中创建自定义中间件(若不存在则用 php artisan make:middleware ApiThrottleRequests 生成)。
继承 Illuminate\Routing\Middleware\ThrottleRequests,重写 buildResponse() 方法:
protected function buildResponse($key, $maxAttempts) {<br> $retryAfter = $this->getTimeUntilNextRetry($key);<br> return response()->json([<br> 'message' => 'Too many requests',<br> 'retry_after' => (int) $retryAfter,<br> ], 429)<br> ->withHeaders([<br> 'X-RateLimit-Retry-After' => $retryAfter,<br> ]);<br>}
在 app/Http/Kernel.php 的 $routeMiddleware 数组中注册:'throttle.api' => \App\Http\Middleware\ApiThrottleRequests::class,
最后将路由中的中间件从 throttle:api-v1 替换为 throttle.api:api-v1,确保错误响应走自定义逻辑。


















