Rate Limiting en Next.js: Protege tus APIs
Implementa rate limiting en tus API routes de Next.js. Limita requests por IP, protege endpoints sensibles y evita abuso con patrones simples.
Rate Limiting en Next.js: Protege tus APIs
Rate limiting es la primera línea de defensa de tus API routes. Sin el, cualquiera puede hacer miles de requests por segundo a tu endpoint de login, agotar tu cuota de base de datos, o disparar tu factura de Vercel. Es trivial de implementar y crítico para producción.
Si tu app tiene endpoints publicos (login, registro, contacto, webhooks), necesitas rate limiting. Punto.
Rate limiter simple (en memoria)
Para proyectos chicos o entornos de una sola instancia, un Map en memoria es suficiente:
// lib/rate-limit.ts
const requests = new Map<string, { count: number; resetTime: number }>();
export function rateLimit(
ip: string,
limit: number = 10,
windowMs: number = 60_000
): { allowed: boolean; remaining: number } {
const now = Date.now();
const record = requests.get(ip);
// Primera request o ventana expirada
if (!record || now > record.resetTime) {
requests.set(ip, { count: 1, resetTime: now + windowMs });
return { allowed: true, remaining: limit - 1 };
}
// Dentro de la ventana
if (record.count < limit) {
record.count++;
return { allowed: true, remaining: limit - record.count };
}
// límite alcanzado
return { allowed: false, remaining: 0 };
}Usarlo en una API route
// app/api/contacto/route.ts
import { NextResponse } from "next/server";
import { rateLimit } from "@/lib/rate-limit";
export async function POST(request: Request) {
const ip = request.headers.get("x-forwarded-for") ?? "unknown";
const { allowed, remaining } = rateLimit(ip, 5, 60_000); // 5 por minuto
if (!allowed) {
return NextResponse.json(
{ error: "Demasiadas solicitudes. Intenta en un minuto." },
{
status: 429,
headers: { "Retry-After": "60" },
}
);
}
// Procesar la request normalmente
const body = await request.json();
// ... tu lógica aquí
return NextResponse.json(
{ ok: true },
{ headers: { "X-RateLimit-Remaining": String(remaining) } }
);
}Rate limiting con Upstash (serverless)
En serverless (Vercel, Cloudflare), cada invocación puede correr en una instancia diferente. Un Map en memoria no sirve porque cada instancia tiene su propio Map. Necesitas un store compartido.
Upstash tiene Redis serverless con un plan gratuito de 10K requests/día:
npm install @upstash/ratelimit @upstash/redis// lib/rate-limit.ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
export const rateLimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "60 s"), // 10 requests por minuto
analytics: true,
});// app/api/login/route.ts
import { NextResponse } from "next/server";
import { rateLimit } from "@/lib/rate-limit";
export async function POST(request: Request) {
const ip = request.headers.get("x-forwarded-for") ?? "unknown";
const { success, remaining } = await rateLimit.limit(ip);
if (!success) {
return NextResponse.json(
{ error: "Demasiados intentos" },
{ status: 429, headers: { "Retry-After": "60" } }
);
}
// ... tu lógica de login
}Dos líneas de config, una línea para verificar. Upstash se encarga del almacenamiento distribuido.
Rate limiting en middleware (global)
Si quieres proteger TODAS tus API routes:
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const requests = new Map<string, { count: number; resetTime: number }>();
export function middleware(request: NextRequest) {
// Solo aplicar a API routes
if (!request.nextUrl.pathname.startsWith("/api")) {
return NextResponse.next();
}
const ip = request.headers.get("x-forwarded-for") ?? "unknown";
const now = Date.now();
const record = requests.get(ip);
if (!record || now > record.resetTime) {
requests.set(ip, { count: 1, resetTime: now + 60_000 });
return NextResponse.next();
}
if (record.count >= 30) { // 30 requests por minuto global
return NextResponse.json(
{ error: "Rate limit exceeded" },
{ status: 429 }
);
}
record.count++;
return NextResponse.next();
}límites recomendados
| Endpoint | límite | Razon |
|---|---|---|
| Login | 5/min por IP | Prevenir brute force |
| Registro | 3/min por IP | Prevenir spam de cuentas |
| Contacto | 2/min por IP | Prevenir spam |
| API general | 30/min por IP | Uso normal |
| Webhooks | 100/min por IP | Los servicios envian rafagas |
Siguiente paso
Rate limiting es una capa de seguridad. Para protección más completa, revisa la guía de seguridad en aplicaciones Next.js y la guía de headers de seguridad para CSP, HSTS y demas.
Preguntas frecuentes
¿Qué es rate limiting?
Es una técnica que limita cuántas requests puede hacer un usuario o IP en un período de tiempo. Si alguien hace 100 requests por segundo a tu API, el rate limiter bloquea las requests que excedan el límite y responde con un 429 Too Many Requests.
¿Necesito rate limiting si uso Vercel?
Si. Vercel tiene protecciones básicas contra DDoS, pero no limita el uso de tus API routes. Un bot puede hacer miles de requests a tu endpoint de login, y tu pagas por cada invocación de serverless function.
¿Donde guardo el contador de requests?
Para una sola instancia, un Map en memoria funciona. Para múltiples instancias o serverless, necesitas un store externo como Redis (Upstash tiene un plan gratuito) o una base de datos.
¿Qué código HTTP devuelvo cuando limito?
429 Too Many Requests. Incluye un header Retry-After con los segundos que el cliente debe esperar antes de reintentar.
Articulos relacionados
Row Level Security en Supabase: Errores Comunes que Dejan tu Base de Datos Abierta
Los 5 errores más comunes de Row Level Security en Supabase que dejan tu base de datos expuesta. USING(true), tablas sin RLS, service_role en el cliente y cómo corregirlos.
OWASP Top 10: Guía Práctica para Desarrolladores Web
Guía práctica del OWASP Top 10 en español. Las 10 vulnerabilidades más críticas en aplicaciones web con ejemplos de código, prevención en Next.js y Node.js, y checklist de seguridad.
Archivos .env Expuestos: Cómo Verificar si tu Sitio Filtra Secretos
Guía para detectar si tu sitio web expone archivos .env, .git y configuraciones sensibles. Verificación manual, protección en Next.js y Vercel, y remediación.