> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Personalización del rate limiting

> 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.

## Cómo funciona la facade `RateLimiter`

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`.

```php theme={null}
<?php

namespace 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`.

## Rate limits por usuario, por IP y por plan

### Diferenciar entre usuarios autenticados e invitados

```php theme={null}
RateLimiter::for('uploads', function (Request $request) {
    return $request->user()
        ? Limit::perHour(100)->by($request->user()->id)
        : Limit::perHour(10)->by($request->ip());
});
```

### Limitar según el plan del usuario

```php theme={null}
RateLimiter::for('api', function (Request $request) {
    $user = $request->user();

    if (! $user) {
        return Limit::perMinute(30)->by($request->ip());
    }

    return match ($user->plan) {
        'enterprise' => Limit::none(),
        'pro'        => Limit::perMinute(500)->by($user->id),
        default      => Limit::perMinute(60)->by($user->id),
    };
});
```

### Limitación global por IP

Aplica throttling a nivel de IP, con independencia del endpoint concreto.

```php theme={null}
RateLimiter::for('global', function (Request $request) {
    return Limit::perMinute(1000)->by($request->ip());
});
```

### Combinar varios límites

Si devuelves un array, se evalúan todos los límites. Cuando cualquiera se supera, se devuelve `429`.

```php theme={null}
RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(10)->by($request->ip()),
        Limit::perMinute(5)->by($request->input('email')),
    ];
});
```

<Info>
  Si defines varios límites con el mismo valor `by`, añade prefijos para que las claves no colisionen.
</Info>

```php theme={null}
RateLimiter::for('uploads', function (Request $request) {
    return [
        Limit::perMinute(10)->by('minute:' . $request->user()->id),
        Limit::perDay(1000)->by('day:' . $request->user()->id),
    ];
});
```

## Middleware `throttle` y nombre del limitador personalizado

Pasa al middleware `throttle` el nombre del limitador definido.

```php theme={null}
use Illuminate\Support\Facades\Route;

Route::middleware(['throttle:api'])->group(function () {
    Route::get('/user', function () { /* ... */ });
    Route::post('/posts', function () { /* ... */ });
});
```

### Registro en `bootstrap/app.php`

Desde Laravel 11, los middlewares se gestionan desde `bootstrap/app.php`.

```php theme={null}
use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        apiPrefix: 'api',
    )
    ->withMiddleware(function (\Illuminate\Foundation\Configuration\Middleware $middleware): void {
        $middleware->throttleApi('api');
    })
    ->create();
```

## Ejemplo de aplicación en rutas de API

<Steps>
  <Step title="Define los limitadores">
    Define varios limitadores en `AppServiceProvider`.

    ```php theme={null}
    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')),
            ];
        });
    }
    ```
  </Step>

  <Step title="Aplica el middleware a las rutas">
    ```php theme={null}
    // routes/api.php
    use Illuminate\Support\Facades\Route;

    Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
        Route::get('/user', [\App\Http\Controllers\UserController::class, 'show']);
        Route::get('/posts', [\App\Http\Controllers\PostController::class, 'index']);
    });

    Route::middleware(['auth:sanctum', 'throttle:uploads'])->group(function () {
        Route::post('/uploads', [\App\Http\Controllers\UploadController::class, 'store']);
    });

    Route::middleware(['throttle:login'])->group(function () {
        Route::post('/login', [\App\Http\Controllers\AuthController::class, 'login']);
    });
    ```
  </Step>
</Steps>

## Cabeceras de respuesta (`X-RateLimit-*`)

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 theme={null}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
Retry-After: 45
X-RateLimit-Reset: 1717000000
Content-Type: application/json

{
    "message": "Too Many Requests."
}
```

### Devolver una respuesta personalizada

```php theme={null}
RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)
        ->by($request->user()?->id ?: $request->ip())
        ->response(function (Request $request, array $headers) {
            return response()->json([
                'message' => 'Has superado el límite de peticiones. Inténtalo de nuevo en unos instantes.',
                'retry_after' => $headers['Retry-After'],
            ], 429, $headers);
        });
});
```

## Comprobación manual con `RateLimiter::attempt()`

Si quieres comprobar el rate limit en cualquier punto del código sin usar el middleware `throttle`, utiliza `RateLimiter::attempt()`.

```php theme={null}
use Illuminate\Support\Facades\RateLimiter;

class SmsController extends Controller
{
    public function send(Request $request): \Illuminate\Http\JsonResponse
    {
        $key = 'sms:' . $request->user()->id;

        $executed = RateLimiter::attempt(
            key: $key,
            maxAttempts: 5,
            callback: function () use ($request) {
                app(SmsService::class)->send(
                    $request->user()->phone,
                    $request->input('message')
                );
            },
            decaySeconds: 3600,  // 1 hora
        );

        if (! $executed) {
            $seconds = RateLimiter::availableIn($key);

            return response()->json([
                'message' => "Has superado el límite de envío de SMS. Reintenta en {$seconds} segundos.",
            ], 429);
        }

        return response()->json(['message' => 'SMS enviado.']);
    }
}
```

### Comprobación y reset del contador

```php theme={null}
// 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);
```

### Ejemplo de throttling de login

```php theme={null}
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');
}
```

## Rate limiting según la respuesta

Para contar solo respuestas específicas, usa `after()`. Ejemplo que solo cuenta las respuestas 404 para prevenir enumeración de recursos:

```php theme={null}
use Symfony\Component\HttpFoundation\Response;

RateLimiter::for('resource-lookup', function (Request $request) {
    return Limit::perMinute(10)
        ->by($request->user()?->id ?: $request->ip())
        ->after(function (Response $response) {
            return $response->getStatusCode() === 404;
        });
});
```

## Rate limiting con Redis

Basta con cambiar el driver de caché por defecto a Redis para que el middleware `throttle` lo utilice también.

### Configuración del driver Redis

```php theme={null}
// config/cache.php
'default' => env('CACHE_DRIVER', 'redis'),
```

```ini theme={null}
# .env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
```

### Usar `throttleWithRedis`

Para usar el middleware de throttling optimizado para Redis, llama a `throttleWithRedis()` en `bootstrap/app.php`.

```php theme={null}
use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        apiPrefix: 'api',
    )
    ->withMiddleware(function (\Illuminate\Foundation\Configuration\Middleware $middleware): void {
        $middleware->throttleWithRedis();
    })
    ->create();
```

Con esto, el middleware `throttle` se mapea a la clase `ThrottleRequestsWithRedis` y usa operaciones atómicas de Redis para contar con precisión.

<Warning>
  Al usar `throttleWithRedis()`, asegúrate de que Redis esté disponible. Si la conexión falla, podrías rechazar todas las peticiones.
</Warning>

### Ventajas de usar Redis

* **Escalado horizontal** — puedes compartir contadores entre varias instancias.
* **Alta precisión** — evita race conditions con operaciones atómicas.
* **Gestión de TTL** — usa el TTL nativo de Redis para eliminar contadores automáticamente.

## Páginas relacionadas

<Card title="Caché" icon="database" href="/es/cache">
  Consulta la configuración y uso de los drivers de caché de Laravel, incluido Redis.
</Card>


## Related topics

- [Laravel Fortify y los starter kits](/es/advanced/fortify.md)
- [Actualización de Laravel — Marzo de 2026](/es/blog/changelog/202603.md)
- [Actualización de Laravel 10 a 11](/es/blog/upgrade-10-to-11.md)
- [Crear un servidor MCP con Laravel](/es/advanced/mcp-server.md)
- [Estructura de aplicación en Laravel 11 y posteriores](/es/advanced/app-structure.md)
