La autenticación responde a una pregunta: quién es este. Todo lo demás con lo que se encuentra una petición de camino hacia dentro responde a otra distinta: cuánto de tu API puede usar, en qué idioma, con qué tamaño, desde qué origen y qué registro sobrevive después.
Esas preocupaciones comparten una propiedad que las hace merecedoras de un capítulo propio: ninguna pertenece a un endpoint concreto. Se deciden una vez, en el orden en que corre el middleware, y a partir de ahí son ciertas en todas partes. Equivoca el orden y un límite de frecuencia no protege nada porque el trabajo ya se hizo; olvida uno y un solo endpoint se comporta calladamente distinto de los otros noventa y nueve.
Limite de frecuencia: throttle:api-token
El límite de frecuencia es la defensa de tu API contra el abuso y el agotamiento de recursos. Sin él, un solo cliente mal configurado o malicioso puede machacar tu backend y degradar el rendimiento de todos los demás. El middleware throttle:api-token es el sistema de límite integrado de Laravel, configurado específicamente para limitar peticiones por token Bearer.
El limitador se registra una vez, en el service provider del paquete compartido, así que todos los servicios de la flota limitan de forma idéntica sin copiar la regla:
RateLimiter::for('api-token', function (Request $request) {
/** @var int $limit */
$limit = config('webplo.rate_limit', 60);
return Limit::perMinute($limit)->by($request->bearerToken() ?: $request->ip());
});
Esto hace tres cosas: define un limitador llamado 'api-token', toma el techo de la configuración en lugar de fijarlo a fuego, y ata el límite al token Bearer cuando existe, recurriendo a la IP del cliente cuando no.
Fíjate en lo que hace y lo que no hace ese respaldo. Una petición sin token se limita por IP, no se rechaza: rechazarla es trabajo del middleware de autenticación, que corre antes y nunca deja que una petición sin autenticar llegue siquiera al limitador. Darle dos trabajos al limitador es como acabas con un limitador que autoriza calladamente.
La parte crucial es by($request->bearerToken() ?: $request->ip()). Cada token Bearer tiene su propio contador de cuota. Si el Cliente A usa el token abc123, tiene sesenta peticiones por minuto. Si el Cliente B usa def456, tiene otras sesenta, independientes. Ese aislamiento es crítico: impide que un cliente monopolice tu API.
El límite es enteramente de caché. Cuando llega una petición, el middleware mira la clave api-token:{token} e incrementa un contador. Si supera sesenta dentro de la ventana de un minuto, la petición se rechaza con un 429. El backend de caché expira automáticamente el contador tras un minuto, reiniciando la cuota.
Por eso importa tu configuración de caché. Si usas una caché en base de datos en una API con mucho tráfico, las operaciones de caché se vuelven un cuello de botella. Redis es dramáticamente más rápido. Para la mayoría de APIs en producción, Redis o Memcached es la elección correcta.
Cuando un cliente alcanza el límite:
- Laravel lanza una
ThrottleRequestsException - La excepción se captura en tu manejador (ver
bootstrap/app.php) - El middleware devuelve un 429 Too Many Requests
- El cliente recibe un error y debería dejar de enviar peticiones
Y algo importante: el límite detiene la petición antes de que tu controlador corra siquiera. No procesas la petición, no consultas tu base de datos, no llamas a APIs externas. La rechazas de inmediato. Eso es eficiente y protege tu backend del daño que puede causar una avalancha.
Los clientes inteligentes no dependen del código de estado para saber que están siendo limitados. Comprueban proactivamente las cabeceras de límite (que aporta AddRateLimitHeadersMiddleware) y aflojan antes de chocar. Ven X-RateLimit-Remaining: 5 y saben que deben frenar. Ven X-RateLimit-Reset: 1708899234 y saben cuándo se reinicia su cuota.
Esa es la diferencia entre un cliente reactivo (espera a recibir un 429 y entonces afloja) y uno proactivo (mira las cabeceras y afloja antes de chocar). Las cabeceras de tu API son las que hacen posibles a los clientes proactivos.
Sesenta peticiones por minuto es razonable para la mayoría de APIs internas, pero puedes ajustarlo. Para más caudal, cámbialo a Limit::perMinute(300). Para más rigor, a Limit::perMinute(10). También puedes usar perSecond(), perHour() o ventanas propias.
Si necesitas límites distintos para endpoints distintos, define varios limitadores:
RateLimiter::for('api-token', function (Request $request) {
return Limit::perMinute(60)->by($request->bearerToken() ?: $request->ip());
});
RateLimiter::for('api-token-strict', function (Request $request) {
return Limit::perMinute(10)->by($request->bearerToken() ?: $request->ip());
});
Y luego usa 'throttle:api-token-strict' en los endpoints más caros o sensibles.
Cuando se superan los límites, se reporta una excepción a tu sistema de seguimiento de errores:
if ($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) {
Nightwatch::warning('Rate limit exceeded', [
'exception' => class_basename($exception),
]);
}
Eso te da visibilidad sobre qué clientes están chocando con los límites y con qué frecuencia. Si un cliente choca repetidamente, puede indicar una mala configuración de su lado, o que están atacando tu API. En cualquier caso, tienes los datos para investigar y responder.