Rate Limiting
Protege tus endpoints contra abuso con rate limiting
Rate Limiting
Sin rate limiting, un atacante puede hacer miles de peticiones por segundo a tus API routes: fuerza bruta en login, spam en formularios o agotar tu cuota de Stripe/Resend.
Stack recomendado
Usamos Upstash Redis con @upstash/ratelimit. Es serverless, funciona perfecto con Vercel y tiene un generous free tier.
Setup
Crear base de datos en Upstash
- Ve a upstash.com y crea una cuenta
- Click en Create Database
- Selecciona la region mas cercana a tu deploy (ej:
eu-west-1para Europa) - Copia las credenciales
Agregar variables de entorno
UPSTASH_REDIS_REST_URL="https://xxx.upstash.io"
UPSTASH_REDIS_REST_TOKEN="AXxx..."Instalar dependencias
npm install @upstash/redis @upstash/ratelimitCrear utilidad de rate limiting
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
// 10 peticiones por 10 segundos por IP
export const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, "10 s"),
analytics: true,
prefix: "ratelimit",
});
// Rate limit mas estricto para auth
export const authRatelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(5, "60 s"),
analytics: true,
prefix: "ratelimit:auth",
});
// Rate limit para webhooks (mas permisivo)
export const webhookRatelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(100, "60 s"),
analytics: true,
prefix: "ratelimit:webhook",
});Usar en API routes
Proteger un endpoint
import { NextRequest, NextResponse } from "next/server";
import { ratelimit } from "@/lib/rate-limit";
export async function POST(request: NextRequest) {
// Identificar al usuario por IP
const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success, limit, reset, remaining } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: "Demasiadas peticiones. Intenta de nuevo mas tarde." },
{
status: 429,
headers: {
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
},
}
);
}
// ... procesar la peticion
return NextResponse.json({ success: true });
}Proteger rutas de autenticación
import { NextRequest, NextResponse } from "next/server";
import { authRatelimit } from "@/lib/rate-limit";
export async function POST(request: NextRequest) {
const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success } = await authRatelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: "Demasiados intentos de login. Espera 1 minuto." },
{ status: 429 }
);
}
// ... lógica de autenticación
}Rate limiting en Middleware
Para proteger multiples rutas a la vez, puedes usar el middleware de Next.js:
import { NextRequest, NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(20, "10 s"),
analytics: true,
});
export async function middleware(request: NextRequest) {
// Solo aplica rate limiting a rutas de API
if (request.nextUrl.pathname.startsWith("/api")) {
const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1";
const { success } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: "Rate limit exceeded" },
{ status: 429 }
);
}
}
return NextResponse.next();
}
export const config = {
matcher: "/api/:path*",
};El middleware se ejecuta en el Edge Runtime. Verifica que tus dependencias sean compatibles. @upstash/redis y @upstash/ratelimit funcionan en Edge sin problemas.
Algoritmos disponibles
| Algoritmo | Uso recomendado |
|---|---|
slidingWindow | Uso general, distribuye las peticiones de forma uniforme |
fixedWindow | Mas simple, resetea el contador en intervalos fijos |
tokenBucket | Permite rafagas cortas seguidas de un ritmo constante |
// Sliding Window: 10 peticiones cada 10 segundos
Ratelimit.slidingWindow(10, "10 s")
// Fixed Window: 100 peticiones por minuto
Ratelimit.fixedWindow(100, "60 s")
// Token Bucket: 10 tokens, se recarga 1 por segundo
Ratelimit.tokenBucket(10, "1 s", 10)Limites recomendados por tipo de endpoint
| Endpoint | Limite | Ventana |
|---|---|---|
| API general | 20 peticiones | 10 segundos |
| Login / Registro | 5 peticiones | 60 segundos |
| Envio de emails | 3 peticiones | 60 segundos |
| Checkout de Stripe | 5 peticiones | 60 segundos |
| Webhooks | 100 peticiones | 60 segundos |
Upstash ofrece 10,000 peticiones/dia gratis. Mas que suficiente para un SaaS en etapa inicial.