Cómo arreglar errores CORS (y por qué ocurren)
Un error CORS significa que tu página web intentó leer una respuesta de un origen distinto (otro dominio, puerto o protocolo), y ese servidor no envió cabeceras que lo permitan. El navegador bloquea la respuesta para proteger al usuario. Lo arreglas en el servidor, devolviendo una cabecera Access-Control-Allow-Origin para el origen de tu sitio y gestionando las peticiones preflight OPTIONS, o lo evitas enviando la petición a través de tu propio backend en el mismo origen.
Es uno de los errores más confusos del desarrollo web, porque a menudo la petición sí llega al servidor. Esta guía explica qué está pasando y cómo arreglar cada variante.
Qué significa «origen»
Un origen es la combinación de protocolo, dominio y puerto:
| URL | Origen |
|---|---|
https://miapp.com/dashboard | https://miapp.com |
https://api.miapp.com/users | https://api.miapp.com (otro subdominio, así que otro origen) |
http://localhost:5173 | http://localhost:5173 |
http://localhost:3000 | http://localhost:3000 (otro puerto, así que otro origen) |
Los navegadores siguen la política del mismo origen: el JavaScript de una página puede leer libremente las respuestas de su propio origen, pero no de otros. CORS (Cross-Origin Resource Sharing, compartición de recursos entre orígenes) es el sistema que permite a un servidor optar por ello: «las respuestas mías pueden ser leídas por páginas de estos orígenes».
Dos consecuencias sorprenden a la gente:
- La petición a menudo llega al servidor. CORS suele impedir que tu código lea la respuesta, no que el servidor reciba la petición. Un POST puede haberse guardado aunque tú veas un error.
- Solo los navegadores lo aplican. curl, Postman y el código del lado del servidor ignoran CORS por completo. Por eso que «funcione en Postman» no te dice nada sobre CORS.
Paso 1: lee el error exacto
Abre las herramientas para desarrolladores (clic derecho → Inspeccionar), ve a la Consola y lee el mensaje completo. La redacción de Chrome te indica qué problema tienes:
| El mensaje contiene | Qué está mal |
|---|---|
No 'Access-Control-Allow-Origin' header is present | El servidor no envió la cabecera, o la petición falló antes de llegar a tu código CORS (por ejemplo, un 404 o un 500) |
The 'Access-Control-Allow-Origin' header has a value '...' that is not equal to the supplied origin | El servidor permite un origen distinto del tuyo |
Response to preflight request doesn't pass access control check | La petición preflight OPTIONS falló |
...must not be the wildcard '*' when the request's credentials mode is 'include' | Envías cookies, y el servidor respondió con * |
Request header field authorization is not allowed | El servidor no incluyó esa cabecera en Access-Control-Allow-Headers |
Después abre la pestaña Red (Network), busca la petición que falla (y cualquier petición OPTIONS anterior), y revisa su código de estado y las cabeceras de respuesta.
Mira primero el código de estado. Muchos «errores CORS» son en realidad un error del servidor disfrazado. Si el servidor se cae o devuelve un 404, su respuesta de error a menudo no tiene cabeceras CORS, así que el navegador informa de CORS en lugar del problema real. Arregla el 500 o el 404, y el error CORS puede desaparecer.
Paso 2: decide si deberías llamar a esa API desde el navegador
Antes de añadir cabeceras, pregúntate: ¿debería el navegador llamar directamente a esta API?
- Una API de terceros que necesita una clave secreta (OpenAI, la clave secreta de Stripe, la mayoría de servicios de correo) nunca debería llamarse desde el navegador. La clave sería visible para cada visitante. Llámala desde tu backend, y el problema CORS desaparece, porque las peticiones de servidor a servidor no están sujetas a CORS. Consulta cómo mantener seguras tus claves de API.
- Una API de terceros que no controlas y que no envía cabeceras CORS no puede arreglarse desde tu lado. Llámala desde tu backend.
- Tu propia API puede arreglarse configurándola correctamente. Continúa abajo.
Paso 3: arréglalo en tu servidor
El servidor debe añadir las cabeceras correctas a sus respuestas, incluidas las de error.
Para la petición real, envía:
Access-Control-Allow-Origin: https://miapp.com(el origen exacto de tu frontend, sin barra final)Vary: Origin, si el valor depende de qué origen lo pidió
Para la petición preflight OPTIONS, responde con un estado de éxito (200 o 204) y:
Access-Control-Allow-Origin: https://miapp.comAccess-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETEAccess-Control-Allow-Headers: Content-Type, Authorization(cada cabecera personalizada que envíes)- Opcionalmente
Access-Control-Max-Age: 600, para que el navegador guarde el preflight en caché durante un tiempo
La mayoría de frameworks tienen un paquete o un ajuste que hace esto. En Express, el paquete cors; en FastAPI, CORSMiddleware; en ASP.NET Core, AddCors; en Spring, @CrossOrigin o una configuración de CORS. Una configuración típica en Express:
import cors from "cors";
app.use(cors({
origin: ["https://miapp.com", "http://localhost:5173"],
credentials: true,
}));
Coloca la gestión de CORS antes de tus rutas y de la autenticación, para que las peticiones preflight (que no llevan inicio de sesión) no sean rechazadas con un 401.
Con un constructor de IA, describe la configuración con precisión:
El frontend en
https://miapp.comllama a la API enhttps://api.miapp.comy recibe «Response to preflight request doesn't pass access control check». Configura CORS en la API para permitir exactamentehttps://miapp.comy el origen de la vista previa, permitir GET, POST, PATCH, DELETE con las cabeceras Content-Type y Authorization, gestionar OPTIONS antes de la autenticación e incluir también las cabeceras CORS en las respuestas de error.
Paso 4: gestiona las cookies y las credenciales
Si tu frontend envía cookies (por ejemplo, una sesión de inicio de sesión) a otro origen, ambos lados deben estar de acuerdo:
- El frontend establece
credentials: "include"enfetch, owithCredentials: trueen axios. - El servidor envía
Access-Control-Allow-Credentials: true. - La cabecera
Access-Control-Allow-Origindel servidor debe ser el origen exacto, no*.
Las cookies entre sitios también necesitan SameSite=None; Secure en la cookie, y algunos navegadores restringen aún más las cookies de terceros. Es una razón más para preferir el enfoque del Paso 5.
Paso 5: evita CORS con una configuración del mismo origen
A menudo, la solución más limpia es no hacer peticiones entre orígenes. Sirve la API desde el mismo origen que la página, por ejemplo en https://miapp.com/api/...:
- En desarrollo, la mayoría de servidores de desarrollo pueden actuar como proxy. La opción
server.proxyde Vite puede reenviar/apia tu backend en otro puerto, así que el navegador solo habla con un origen. - En producción, dirige
/apial backend mediante las opciones de reescritura o proxy de tu hosting, o sirve el frontend y la API desde un mismo servidor.
El frontend entonces llama a una ruta relativa como fetch("/api/orders"). No hay petición entre orígenes, ni preflight entre orígenes, ni problemas con cookies. Además, no tienes que codificar un dominio de API que sea distinto entre la vista previa y la producción.
Cambia el frontend para llamar al backend con rutas relativas /api en lugar de una URL completa, y configura un proxy de desarrollo para que /api se reenvíe al servidor de la API. Después elimina los ajustes de CORS que ya no necesitamos.
Lo que no debes hacer
mode: "no-cors"enfetch. No salta nada; le da a tu código una respuesta vacía e ilegible.- Extensiones del navegador que desactivan CORS. Solo cambian tu navegador. Cada visitante seguirá viendo el error.
- Servicios públicos de «proxy CORS» para apps reales. Pueden leer y modificar todo lo que pasa por ellos, y dejan de funcionar.
Access-Control-Allow-Origin: *en una API que usa inicios de sesión. No funciona con credenciales de todos modos, y es el valor por defecto equivocado para datos privados.- Reflejar cualquier cabecera
Originde vuelta con credenciales permitidas. Eso permite que cualquier sitio web haga peticiones autenticadas en nombre de tus usuarios. Usa una lista de permitidos.
Causas habituales en apps creadas con IA
| Situación | Solución |
|---|---|
| El frontend llama a una API de terceros con una clave secreta | Mueve la llamada al backend |
| Funciona en la vista previa, falla en el dominio publicado | Añade a la lista de permitidos los orígenes publicado y de dominio propio, o usa rutas relativas /api |
| El error solo aparece en POST, PUT o DELETE | El preflight no se gestiona; añade el manejo de OPTIONS y las cabeceras permitidas |
| El error apareció al añadir el inicio de sesión | Las credenciales requieren un origen exacto y Allow-Credentials |
| Error «CORS» pero la pestaña Red muestra 500 | Arregla primero el error del servidor |
URL de la API fija en localhost | Usa rutas relativas o un ajuste por entorno |
CORS en Mythex
Cuando un proyecto de Mythex tiene un sitio web y un servidor de API, ambos se ejecutan como servicios en el mismo proyecto, y puedes cambiar entre ellos en la vista previa. Pedir al agente que el frontend llame al backend mediante una ruta relativa /api, con proxy en desarrollo, evita la mayoría de problemas de CORS. Si de verdad necesitas llamadas entre orígenes, dale al agente el error exacto de la consola y los orígenes implicados. Comprueba el resultado también en la app publicada, ya que su dirección es distinta a la de la vista previa. Consulta Servicios múltiples y Backends de API en la documentación, y nuestras guías sobre cómo depurar una app creada con IA y frontend frente a backend.
Lista de comprobación de CORS
- Leído el mensaje completo de la consola y el código de estado de la petición que falla
- Descartado un 404 o un 500 oculto
- APIs con clave secreta llamadas desde el backend, no desde el navegador
- El servidor envía
Access-Control-Allow-Origincon los orígenes exactos permitidos - Preflight
OPTIONSrespondido con los métodos y cabeceras permitidos, antes de la autenticación - Cabeceras CORS presentes también en las respuestas de error
- Credenciales: origen exacto,
Allow-Credentials: truey ajustes de cookies correctos - Cubiertos los orígenes de vista previa, publicado y dominio propio, o usadas rutas relativas
/api - Sin
no-cors, proxies públicos ni atajos de reflejar cualquier origen
Preguntas
¿Qué significa un error CORS?
Tu página, cargada desde un origen, intentó leer una respuesta de otro origen, y ese servidor no incluyó las cabeceras que lo permiten. El navegador impide que tu código lea la respuesta. La solución está en el servidor, o en cómo se enruta la petición, no en el código del frontend.
¿Puedo arreglar un error CORS desde el frontend?
No de forma correcta. Ajustes como mode: 'no-cors' solo ocultan la respuesta a tu código. Lo arreglas haciendo que el servidor envíe la cabecera Access-Control-Allow-Origin adecuada, o llamando a la API desde tu propio backend o desde un proxy del mismo origen.
¿Es seguro Access-Control-Allow-Origin: *?
Está bien para datos públicos que cualquiera puede leer, como una API pública sin inicio de sesión. No puede usarse con cookies ni con otras credenciales, y para APIs privadas debes listar los orígenes exactos que están permitidos.
¿Por qué mi petición funciona en Postman o curl pero no en el navegador?
CORS solo lo aplican los navegadores. Herramientas como Postman y curl no lo tienen en cuenta, así que una petición puede funcionar allí y aun así ser bloqueada cuando el JavaScript de la página la hace.
¿Qué es una petición preflight?
Para las peticiones que no son simples, como un POST con JSON o una que lleva una cabecera Authorization, el navegador envía primero una petición OPTIONS para preguntar al servidor qué métodos y cabeceras están permitidos. Si esa petición OPTIONS falla o no tiene las cabeceras adecuadas, la petición real nunca se envía.