SEGURIDAD

CORS

Mecanismo del navegador que controla qué dominios pueden hacer peticiones a tu API desde JavaScript. Causa frecuente del error 'Access-Control-Allow-Origin' en desarrollo.

Nivel · intermedio4 min de lecturaActualizado 22 may 2026
También conocido como: Cross-Origin Resource Sharing

Definición

CORS (siglas de Cross-Origin Resource Sharing) es un mecanismo de seguridad implementado por los navegadores que controla qué dominios pueden hacer peticiones HTTP a tu servidor desde JavaScript. Existe para proteger a los usuarios: sin CORS, cualquier web maliciosa podría hacer peticiones a tu cuenta bancaria mientras tienes la sesión abierta.

Por defecto, el navegador aplica la same-origin policy: el JavaScript de tudominio.com solo puede hacer fetch a tudominio.com. Si quieres permitir que otrodominio.com haga peticiones a tu API, tienes que decirlo explícitamente con cabeceras HTTP especiales — eso es CORS.

Es la causa #1 de frustración cuando un dev frontend intenta llamar a una API externa por primera vez. El típico error en consola:

Access to fetch at 'https://api.midominio.com/datos' from origin 
'http://localhost:3000' has been blocked by CORS policy

Cómo funciona

CORS define headers HTTP que el servidor debe enviar para autorizar peticiones cross-origin:

Header de respuestaFunción
Access-Control-Allow-OriginQué dominios pueden llamar (* para todos, o un dominio concreto)
Access-Control-Allow-MethodsQué métodos HTTP están permitidos (GET, POST, etc.)
Access-Control-Allow-HeadersQué headers personalizados acepta
Access-Control-Allow-CredentialsSi acepta cookies/auth en cross-origin
Access-Control-Max-AgeCuánto cachear el resultado del preflight

Para peticiones "simples" (GET/POST básicas), el navegador envía la petición directamente y mira las cabeceras de respuesta. Si no autorizan tu origen, bloquea la respuesta.

Para peticiones "complejas" (PUT, DELETE, custom headers, body JSON...), el navegador envía un preflight: una petición OPTIONS previa para preguntar "¿me dejas hacer esto?". Solo si la respuesta es afirmativa, hace la petición real.

Ejemplo práctico

API Node.js (Express) configurando CORS:

import express from 'express';
import cors from 'cors';

const app = express();

// CORS abierto a todos (solo desarrollo)
app.use(cors());

// CORS restrictivo en producción
app.use(cors({
  origin: ['https://imdica.es', 'https://www.imdica.es'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400 // cachea preflight 24h
}));

app.get('/api/productos', (req, res) => {
  res.json({ productos: [...] });
});

Lo que el navegador ve:

Petición:
  OPTIONS /api/productos HTTP/1.1
  Origin: https://imdica.es
  Access-Control-Request-Method: GET
  Access-Control-Request-Headers: Authorization

Respuesta del servidor (preflight):
  HTTP/1.1 204 No Content
  Access-Control-Allow-Origin: https://imdica.es
  Access-Control-Allow-Methods: GET, POST, PUT, DELETE
  Access-Control-Allow-Headers: Content-Type, Authorization
  Access-Control-Allow-Credentials: true

Petición real:
  GET /api/productos HTTP/1.1
  Authorization: Bearer xyz

Mitos comunes

  • "CORS es seguridad del servidor": NO. CORS lo aplica el NAVEGADOR. Una petición curl o desde otro servidor ignora completamente CORS. La seguridad real está en autenticación + autorización del servidor.
  • "Si pongo Allow-Origin: * no es seguro": depende. Para APIs públicas sin auth (datos abiertos) es perfectamente válido. El problema solo aparece si combinas * con credentials: true.
  • "CORS es lo mismo que CSRF": no. CSRF es otro vector (peticiones automáticas con cookies). CORS y CSRF se protegen distinto.

Errores comunes y cómo arreglarlos

"No 'Access-Control-Allow-Origin' header is present"

El servidor no envía el header. Soluciones:

  1. Si controlas el servidor: añade el middleware CORS (ej. app.use(cors()) en Express)
  2. Si NO controlas el servidor: usa un proxy. En dev con Next.js puedes usar next.config.js → rewrites para que tu app actúe como proxy
  3. Para testing: extensiones de navegador que desactivan CORS (solo dev, NUNCA en producción ni para usuarios)

"Request header field X is not allowed"

El header personalizado (ej. Authorization) no está en Allow-Headers. Añádelo en la config del servidor.

"The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when credentials mode is 'include'"

No puedes combinar * con cookies/credentials. Especifica el dominio exacto o lista de dominios permitidos.

Funciona en Postman/curl pero no en navegador

Confirma que es CORS. Postman y curl no aplican same-origin policy — son herramientas, no navegadores. Si funciona ahí pero no en navegador, es 100% CORS.

CORS en hosting estático (Hostinger, GitHub Pages...)

Si tu web está estática (HTML+JS sin backend) y llamas a una API externa:

  • La API externa debe permitir tu dominio en CORS
  • No puedes "arreglarlo" tú desde el cliente
  • Si la API no soporta CORS, opciones:
    • Usar un proxy serverless (Cloudflare Workers, Vercel Functions)
    • Pedir al dueño de la API que añada tu dominio
    • JSONP (anticuado, solo GET, evítalo)

Cuándo y cómo configurar CORS

En APIs públicas (datos abiertos sin auth):

Access-Control-Allow-Origin: *

En APIs privadas (con autenticación):

Access-Control-Allow-Origin: https://midominio.com
Access-Control-Allow-Credentials: true

En producción NUNCA * con credentials. Lista los dominios uno a uno.

Referencias

Tagsseguridadnavegadorapifrontend
Escrito por
Antonio Echeverría

Dirijo IMDICA, una empresa de suministro industrial, desde 2007. Escribo estas definiciones desde el lado de quien las usa para decidir, no desde el de quien las estudia.

Si necesitas que alguien construya esto de verdad y no solo lo explique, eso es lo que hago en AE Works.