Rate limiting en Laravel 13: protege tu API con throttle y RateLimiter
En Laravel 13, el rate limiting se configura en minutos con el middleware throttle o con RateLimiter: al superar el límite, tu API responde 429 Too Many Requests con la cabecera Retry-After. Sin él, bots, fuerza bruta y picos de tráfico dejan expuesta tu API.
Qué es el rate limiting y por qué tu API lo necesita
El rate limiting limita cuántas peticiones puede hacer un cliente en una ventana de tiempo. Es la primera línea de defensa de cualquier API: sin él, un bot puede recorrer tus rutas sin control, un cliente mal escrito puede saturar un endpoint y un atacante puede probar contraseñas en tu formulario de login hasta acertar.
El problema: bots, fuerza bruta y picos de tráfico
Los tres escenarios comparten un síntoma: peticiones muy por encima del uso normal. Los bots rastrean y llaman a la API sin pausa, la fuerza bruta prueba credenciales en bucle y un pico de tráfico legítimo puede disparar el coste de un endpoint que procesa datos pesados. El rate limiting pone un techo predecible a cada uno de esos casos.
Cómo responde Laravel: el error 429 Too Many Requests
Cuando un cliente supera el límite, Laravel responde HTTP 429 Too Many Requests e incluye la cabecera Retry-After para indicar cuándo puede reintentar. Es la señal estándar para que el cliente espere antes de volver a llamar, y la respuesta se puede personalizar para que el mensaje explique el límite en tu propio formato.
El middleware throttle: primeros pasos
La forma más directa de aplicar rate limiting en Laravel 13 es el middleware throttle sobre una ruta o un grupo de rutas. No requiere configurar nada más: eliges el límite y la ventana, y el framework se encarga del resto.
Segmentos: throttle:60,1 significa 60 peticiones por minuto
La sintaxis clásica es throttle:60,1: el primer segmento es el número máximo de peticiones y el segundo la ventana en minutos. Un endpoint de búsqueda que consume muchos recursos puede usar throttle:30,1, mientras que una ruta pública de contenido aguanta throttle:300,1. Así se ajusta el límite al coste real de cada operación.
El limiter api que Laravel define por defecto
Las rutas de routes/api.php usan por defecto el limiter api, configurado en el bootstrap de la aplicación para permitir 60 peticiones por minuto por usuario o IP. Ese limiter se aplica con throttle:api y se puede sobrescribir definiendo tu propia versión con RateLimiter::for('api', ...), como verás más adelante.
Aplicar throttle a grupos de rutas en routes/api.php
Para proteger un grupo completo, aplica el middleware en el Route::middleware del grupo. Una API Laravel 13 con Sanctum suele tener el grupo auth:sanctum con throttle:api, de modo que los tokens autenticados también quedan limitados. Si tu aplicación usa tokens de acceso, la guía de Laravel 13 y Sanctum explica cómo montar esa capa de autenticación.
Named limiters con RateLimiter::for()
Cuando el límite por defecto no basta, Laravel permite registrar limitadores con nombre en el método boot de un service provider. Un named limiter centraliza la configuración y la reutiliza en varias rutas con solo citar su nombre.
Registrar limitadores en AppServiceProvider
En AppServiceProvider::boot() se define RateLimiter::for('api', fn ($request) => Limit::perMinute(60)->by(...)). El closure recibe la petición y devuelve el límite real para ese cliente, lo que permite decidir en cada llamada cuántas peticiones se permiten.
Los objetos Limit: perMinute, perHour, perDay y by()
El objeto Limit ofrece ventanas listas para usar: perMinute, perHour y perDay construyen el límite con una sola llamada. El método by() define la clave del contador, es decir, qué identifica a cada cliente: un usuario autenticado, una IP o una combinación de ambos. Cambiar esa clave cambia quién comparte el límite.
Referenciar el limiter por nombre en el middleware
Una vez registrado, el limiter se aplica con throttle:api o el nombre que hayas elegido. El middleware evalúa el closure en cada petición, así que el límite puede variar según el contexto sin tocar las rutas.
Límites dinámicos: por usuario, IP o plan
La potencia del sistema aparece cuando el límite depende de quién llama. Un mismo endpoint puede permitir pocas peticiones a un visitante anónimo y muchas más a un usuario de pago.
by() para distinguir usuarios autenticados de visitantes
El closure puede resolver la clave del contador con by(fn ($request) => $request->user()?->id ?: $request->ip()). Así, un usuario autenticado se cuenta por su ID y un visitante por su IP, evitando que varios usuarios detrás de la misma IP compartan un límite pensado para uno solo.
Límites según el plan (SaaS): un límite por usuario de pago
En una aplicación SaaS, el closure puede consultar el plan del usuario y devolver un Limit distinto: 60 peticiones por minuto para cuentas gratuitas y 600 para planes Enterprise. El mismo middleware protege el endpoint y cada plan recibe el trato que contrató.
Personalizar la respuesta 429 con response()
El objeto Limit acepta response() para devolver un JSON propio cuando se supera el límite, con el mensaje que quieras mostrar al cliente. Es la forma de que el 429 explique el límite en el idioma de tu API en lugar del texto genérico.
Proteger el login contra fuerza bruta
El rate limiting brilla en autenticación: un formulario de login sin límite permite probar contraseñas indefinidamente. Con un limiter ajustado, los intentos fallidos se cortan en pocos segundos.
El patrón de 5 intentos por minuto por email e IP
El patrón típico define un limiter auth de 5 peticiones por minuto y lo aplica a las rutas de login y registro. La clave del contador combina email e IP con by(), de modo que un atacante que rota de IP no esquiva el límite usando siempre el mismo correo.
Combinar throttle con validación y bloqueo de cuenta
El throttle no sustituye a la validación ni al bloqueo de cuenta: los complementa. La validación rechaza entradas inválidas, el rate limiting corta los intentos en bucle y un bloqueo temporal de cuenta castiga los patrones persistentes. Las tres capas juntas convierten un login vulnerable en uno defendido.
Almacén del contador: caché y Redis
El rate limiter necesita un lugar donde guardar los contadores, y ese lugar decide si el límite funciona en un solo servidor o en varios.
El rate limiter usa la caché por defecto
Por defecto, el contador se guarda en el driver de caché configurado en config/cache.php. Para una aplicación en un solo servidor, la caché local es suficiente y no requiere configuración extra.
Redis para límites compartidos entre servidores
Si tu API corre en varios servidores tras un balanceador, la caché local de cada uno no comparte el estado con los demás: cada servidor contaría sus propias peticiones y el límite global se multiplicaría. Usar Redis como almacén del contador hace que el límite sea compartido y consistente en toda la infraestructura.
Buenas prácticas
El rate limiting se diseña por endpoint, no se copia y pega. Unos límites bien elegidos protegen sin romper la experiencia del usuario.
Límites distintos por endpoint: no apliques el mismo a todo
Cada ruta tiene un coste y un uso distinto: una búsqueda pesada tolera 30 peticiones por minuto, una consulta ligera aguanta 300 y un webhook interno puede necesitar más. Definir límites por endpoint evita dos errores: saturar lo costoso y bloquear lo legítimo.
Cabeceras X-RateLimit-Limit y X-RateLimit-Remaining
Informar al cliente de su estado evita sorpresas. Las cabeceras X-RateLimit-Limit y X-RateLimit-Remaining se pueden añadir en la respuesta personalizada, de modo que el consumidor de la API sepa cuánto le queda antes de llegar al 429.
No limitar agresivamente rutas públicas de contenido
Las rutas que sirven contenido público, como la portada o las páginas de un blog, reciben tráfico legítimo de muchos visitantes detrás de IPs compartidas. Aplicarles límites agresivos bloquea a usuarios reales, así que conviene dejarlas con límites amplios y concentrar la protección en los endpoints sensibles, como login y operaciones de escritura.
Conclusión
El rate limiting en Laravel 13 es integrado, configurable y se adapta al caso real: el middleware throttle resuelve el 90 por ciento de los casos, RateLimiter añade límites por usuario, IP o plan, y Redis lo vuelve consistente en varios servidores. Protege tu API desde hoy: define límites por endpoint, blinda el login contra fuerza bruta y deja que el 429 haga su trabajo. Si quieres profundizar en la capa de seguridad, revisa también el tutorial de middleware personalizado en Laravel 13 y sigue leyendo el blog para más guías de desarrollo web.