Profundiza en la facade RateLimiter de Laravel e implementa rate limits personalizados por usuario, por plan o por IP, incluidos ajustes de alta disponibilidad con Redis.
El rate limiting de Laravel se compone de la clase Illuminate\Cache\RateLimiting\Limit y de la facade RateLimiter. Internamente guarda contadores en el driver de caché (por defecto file o Redis) y sigue el número de peticiones.El middleware throttle ejecuta la closure definida con RateLimiter::for() para la petición entrante y, si se supera el límite, devuelve 429 Too Many Requests.
Definir limitadores personalizados en AppServiceProvider
La configuración del rate limiting se hace en el método boot() de App\Providers\AppServiceProvider.
<?phpnamespace App\Providers;use Illuminate\Cache\RateLimiting\Limit;use Illuminate\Http\Request;use Illuminate\Support\Facades\RateLimiter;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{ public function boot(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); }}
El primer argumento de RateLimiter::for() es el nombre del limitador que referenciarás desde el middleware throttle. La closure del segundo argumento debe devolver una instancia de Illuminate\Cache\RateLimiting\Limit.
public function boot(): void{ // Acceso general a la API RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); // Subidas de archivos RateLimiter::for('uploads', function (Request $request) { return $request->user()?->isPro() ? Limit::perHour(500)->by($request->user()->id) : Limit::perHour(50)->by($request->user()?->id ?: $request->ip()); }); // Intentos de login RateLimiter::for('login', function (Request $request) { return [ Limit::perMinute(10)->by($request->ip()), Limit::perMinute(5)->by($request->input('email')), ]; });}
El middleware throttle añade automáticamente la información del límite en las cabeceras.
Cabecera
Descripción
X-RateLimit-Limit
Número de peticiones permitidas.
X-RateLimit-Remaining
Peticiones restantes.
Retry-After
Segundos hasta poder hacer la siguiente petición (solo en 429).
X-RateLimit-Reset
Timestamp UNIX en el que se resetea el límite.
HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 60X-RateLimit-Remaining: 0Retry-After: 45X-RateLimit-Reset: 1717000000Content-Type: application/json{ "message": "Too Many Requests."}
// Número actual de intentos$hits = RateLimiter::attempts($key);// Segundos hasta el siguiente reset$seconds = RateLimiter::availableIn($key);// Si se ha alcanzado el límite$tooMany = RateLimiter::tooManyAttempts($key, $maxAttempts = 5);// Resetear el contador manualmente (tras logout, etc.)RateLimiter::clear($key);
public function login(Request $request): mixed{ $key = 'login:' . $request->input('email'); if (RateLimiter::tooManyAttempts($key, 5)) { $seconds = RateLimiter::availableIn($key); throw ValidationException::withMessages([ 'email' => "Demasiados intentos de login. Reintenta en {$seconds} segundos.", ]); } if (! Auth::attempt($request->only('email', 'password'))) { RateLimiter::hit($key, 300); // Cuenta durante 5 minutos throw ValidationException::withMessages([ 'email' => 'El email o la contraseña no son correctos.', ]); } RateLimiter::clear($key); return redirect()->intended('/dashboard');}