seguridad·6 min de lectura

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 linea 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 critico para produccion.

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:

typescript
// 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 };
  }
 
  // Limite alcanzado
  return { allowed: false, remaining: 0 };
}

Usarlo en una API route

typescript
// 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 logica aqui
 
  return NextResponse.json(
    { ok: true },
    { headers: { "X-RateLimit-Remaining": String(remaining) } }
  );
}

Rate limiting con Upstash (serverless)

En serverless (Vercel, Cloudflare), cada invocacion 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/dia:

bash
npm install @upstash/ratelimit @upstash/redis
typescript
// 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,
});
typescript
// 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 logica de login
}

Dos lineas de config, una linea para verificar. Upstash se encarga del almacenamiento distribuido.

Rate limiting en middleware (global)

Si quieres proteger TODAS tus API routes:

typescript
// 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();
}

Limites recomendados

EndpointLimiteRazon
Login5/min por IPPrevenir brute force
Registro3/min por IPPrevenir spam de cuentas
Contacto2/min por IPPrevenir spam
API general30/min por IPUso normal
Webhooks100/min por IPLos servicios envian rafagas

Siguiente paso

Rate limiting es una capa de seguridad. Para proteccion mas completa, revisa la guia de seguridad en aplicaciones Next.js y la guia de headers de seguridad para CSP, HSTS y demas.

#nextjs#seguridad#rate-limiting#api#middleware

Preguntas frecuentes

Que es rate limiting?

Es una tecnica que limita cuantas requests puede hacer un usuario o IP en un periodo de tiempo. Si alguien hace 100 requests por segundo a tu API, el rate limiter bloquea las requests que excedan el limite y responde con un 429 Too Many Requests.

Necesito rate limiting si uso Vercel?

Si. Vercel tiene protecciones basicas 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 invocacion de serverless function.

Donde guardo el contador de requests?

Para una sola instancia, un Map en memoria funciona. Para multiples instancias o serverless, necesitas un store externo como Redis (Upstash tiene un plan gratuito) o una base de datos.

Que codigo HTTP devuelvo cuando limito?

429 Too Many Requests. Incluye un header Retry-After con los segundos que el cliente debe esperar antes de reintentar.