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