PureTools

CORS Explicado: Por Qué Tu Llamada API Está Bloqueada

PureTools Team· 7 min de lectura
CORS Explicado: Por Qué Tu Llamada API Está Bloqueada

CORS: El Error Que Todo Desarrollador Ha Visto

Estás construyendo un frontend que llama a tu API. Funciona en Postman. Funciona con curl. Pero el navegador dice:

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Esto no es un error del servidor. Es el navegador protegiendo al usuario.

Qué Es Realmente CORS

CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad del navegador. Por defecto, JavaScript corriendo en origen A no puede hacer solicitudes a origen B. Un origen es la combinación de protocolo + dominio + puerto:

URLOrigen
http://localhost:3000http://localhost:3000
https://miapp.comhttps://miapp.com
https://api.miapp.comhttps://api.miapp.com (subdominio diferente = origen diferente)
https://miapp.com:8080https://miapp.com:8080 (puerto diferente = origen diferente)

Cómo Funciona

Para solicitudes simples (GET, POST con content types estándar), el navegador envía la solicitud normalmente pero verifica los headers de la respuesta. Si la respuesta no incluye Access-Control-Allow-Origin, el navegador bloquea a JavaScript de leer la respuesta.

Para solicitudes complejas (PUT, DELETE, headers personalizados, content type JSON), el navegador envía una solicitud preflight primero:

OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

El servidor debe responder con:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Solucionando CORS

Express.js:

import cors from 'cors';

// Permitir todos los orígenes (¡solo desarrollo!)
app.use(cors());

// Producción: especificar orígenes permitidos
app.use(cors({
  origin: ['https://miapp.com', 'https://staging.miapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true, // si usas cookies
}));

API routes Next.js:

export async function GET(request: Request) {
  const data = await getData();
  return Response.json(data, {
    headers: {
      'Access-Control-Allow-Origin': 'https://miapp.com',
      'Access-Control-Allow-Methods': 'GET, POST',
    },
  });
}

Nginx:

location /api/ {
    add_header 'Access-Control-Allow-Origin' 'https://miapp.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';

    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 86400;
        return 204;
    }
}

Errores Comunes

  • Usar * con credentials. Access-Control-Allow-Origin: * no funciona con credentials: true. Debes especificar el origen exacto.
  • Olvidar el handler de OPTIONS. Tu servidor debe responder a solicitudes preflight OPTIONS con headers CORS y status 204.
  • Resolver en el frontend. CORS es configuración server-side. Ningún código frontend puede eludirlo (ese es el objetivo).
  • Proxy en dev, olvidar en prod. Rewrites de next.config.js funcionan en desarrollo pero no en producción. Necesitas headers CORS reales en el servidor de la API.

Alternativas a CORS

  • Proxy same-origin: Enruta llamadas de API a través de tu propio servidor (/api/proxy) para que el navegador vea solicitudes same-origin.
  • Server-side rendering: Busca datos en el servidor (Next.js Server Components), no en el navegador. Sin necesidad de CORS.

Depura tus headers: Referencia HTTP — verifica status codes y headers de tus respuestas de API.