Errores CORS tras el despliegue: causas reales y soluciones
Los errores CORS tras desplegar suelen deberse a fallos de preflight, configuración de credenciales o orígenes comodín. Aprende una lista de verificación repetible para arreglarlos.

Por qué CORS puede romperse de repente después de desplegar
CORS es una regla del navegador, no una regla del servidor. Tu JavaScript de frontend se ejecuta en una pestaña del navegador, y el navegador decide si se le permite leer una respuesta desde un origen distinto (dominio, protocolo o puerto). Si tu backend no devuelve las cabeceras CORS correctas, el navegador bloquea la respuesta incluso si el servidor realmente envió datos.
Por eso los errores CORS tras el despliegue resultan confusos: nada “cambió” en tu código, pero sí cambió el origen. Localmente podrías llamar de http://localhost:5173 a http://localhost:3000. Tras desplegar pasa a ser algo como https://app.yourdomain.com llamando a https://api.yourdomain.com. Dominios distintos, HTTPS y a veces puertos distintos significan un origen distinto, así que el navegador vuelve a comprobar permisos.
Los síntomas comunes suelen parecerse a uno de estos:
- “Blocked by CORS policy” aunque la API funcione en Postman o curl
- Una petición preflight (OPTIONS) falla antes de que se ejecute la petición real
- Las cabeceras CORS faltan en respuestas de error (401/403/500), por lo que solo falla cuando algo sale mal
- Las peticiones con cookies dejan de funcionar de repente
El despliegue además añade piezas móviles invisibles. Un reverse proxy, CDN o una configuración de la plataforma puede sobrescribir cabeceras, eliminarlas o cachear una versión incorrecta. Cambiar a HTTPS puede alterar el comportamiento de las cookies (SameSite y Secure), lo que a su vez afecta las peticiones con “credentials”.
Un ejemplo realista: tu frontend empieza a enviar fetch(..., { credentials: 'include' }) en producción para mantener a los usuarios conectados. Si el backend responde con Access-Control-Allow-Origin: *, el navegador lo rechaza porque los orígenes comodín no se pueden usar con credenciales.
Si heredaste una configuración frontend-backend generada por IA, estos desajustes son comunes. En FixMyMess, la ganancia más rápida suele ser verificar el origen desplegado y luego asegurarse de que cada ruta de respuesta (incluidos errores) devuelva cabeceras CORS consistentes.
Preflight 101: la petición OPTIONS que no notaste
Muchos errores CORS tras el despliegue no tienen que ver con tu llamada real a la API. El navegador a menudo envía primero una silenciosa petición “preflight”. Es una petición OPTIONS que pregunta al servidor: “Si envío esta petición real desde ese origen, ¿me permitirás hacerlo?”
Peticiones simples vs preflight
Algunas peticiones son “simples” y se saltan el preflight. Muchas llamadas modernas de frontend no son simples, así que el navegador las preflightea.
El preflight se desencadena por cosas como:
- Métodos distintos a GET/POST/HEAD (por ejemplo PUT, PATCH, DELETE)
- Enviar JSON con
Content-Type: application/json - Cualquier cabecera personalizada (como
Authorization,X-Requested-WithoX-Api-Key) - Usar opciones de
fetchque añaden cabeceras que no esperabas
Eso significa que puedes llamar directamente a tu endpoint y ver “200 OK”, pero el navegador aún así bloquea la llamada. ¿Por qué? Porque el navegador nunca superó el paso OPTIONS. Si OPTIONS devuelve 404, 401, 500 o faltan cabeceras CORS, la petición real nunca se envía.
Por qué las redirecciones suelen romper el preflight
Un fallo típico solo en producción es una redirección inesperada en OPTIONS. Por ejemplo, tu API puede redirigir HTTP a HTTPS, añadir o quitar “www”, o enviar peticiones no autenticadas a una ruta de login. Los navegadores manejan mal las redirecciones en preflight, y muchos las tratan como fallo aunque la URL redirigida funcionaría.
Un ejemplo práctico: tu frontend en https://app.example.com llama a https://api.example.com. La API está bien, pero OPTIONS a /v1/data devuelve un 301 a /v1/data/ (slash final). Tu GET puede seguir funcionando en pruebas, pero el navegador lo bloquea porque el preflight no recibió una respuesta CORS limpia.
La solución suele ser: asegurar que OPTIONS devuelva un éxito directo (a menudo 204 o 200), incluya las mismas cabeceras CORS que las respuestas reales y nunca redirija.
Orígenes: qué debe coincidir y qué se malinterpreta
Muchos errores CORS tras el despliegue ocurren porque el origen permitido que configuraste no es exactamente la misma cadena que envía el navegador.
Lo que la gente confunde es Origin vs Host. Tu servidor recibe una cabecera Host (a dónde va la petición) y el navegador envía una cabecera Origin (dónde está alojada la página que hace la petición). Las decisiones CORS se basan en Origin, no en Host.
Un origen no es solo un dominio. Es un trío exacto: esquema + dominio + puerto.
Coincidencia exacta de origen (esquema, dominio, puerto)
Si tu frontend corre en https://app.example.com y tu API es https://api.example.com, el navegador enviará Origin: https://app.example.com. Tu backend debe responder con Access-Control-Allow-Origin: https://app.example.com (o con una lista de permitidos en el servidor que resuelva ese valor exacto).
http vs https es la trampa clásica al desplegar. Localmente, podrías probar desde http://localhost:3000. Tras desplegar, el sitio pasa a https://... y tu allowlist sigue incluyendo solo la versión http, así que el navegador lo bloquea.
Desajustes comunes para buscar
Revisa estas pequeñas diferencias que rompen CORS:
https://example.comvshttps://www.example.com- Un puerto faltante:
https://example.comvshttps://example.com:8443 - Dominios de staging:
https://staging.example.comvshttps://app.example.com - Orígenes locales inesperados:
http://127.0.0.1:3000vshttp://localhost:3000 - Builds móviles o de vista previa que usan un dominio diferente al de producción
Un escenario real: tu frontend se sirve desde https://www.brand.com, pero solo permitiste https://brand.com. Todo “parece” igual para un humano, pero para el navegador es un origen distinto.
Si heredaste un backend generado por IA, a menudo tiene una lista de orígenes hardcodeada que nunca se actualizó para el dominio en vivo. FixMyMess suele empezar leyendo los valores Origin reales en los logs de producción y luego alineando la allowlist con esas cadenas exactas.
Cabeceras de respuesta CORS que deben ser correctas siempre
CORS no es una configuración de una sola vez. El navegador comprueba cabeceras específicas en cada petición cross-origin y es estricto con ellas. Si falta una cabecera en una sola ruta (a menudo la ruta OPTIONS), puedes obtener errores CORS que parecen aleatorios.
El navegador principalmente quiere saber: ¿mi Origin está permitido, y para este tipo de petición, están permitidos el método y las cabeceras? Eso significa que tu API debe devolver Access-Control-Allow-Origin que coincida con el origen que pide (por ejemplo https://app.example.com). Si devuelves el origen equivocado, o lo olvidas en algunas endpoints, el navegador bloquea la respuesta aunque el servidor haya enviado un 200.
Los fallos de preflight suelen venir de Access-Control-Allow-Headers. La petición preflight incluye Access-Control-Request-Headers con lo que el navegador planea enviar. Si tu respuesta no incluye cada una de esas cabeceras (faltas comunes: Authorization, Content-Type, X-Requested-With), el navegador se detiene antes de la petición real.
El cache también puede hacer que CORS parezca inconsistente. Si permites múltiples orígenes, añade Vary: Origin para que CDNs y proxies no cacheen la respuesta CORS de un origen y la sirvan luego a otro.
Para preflight OPTIONS, devolver 204 No Content está bien, pero las cabeceras aún deben estar. Una respuesta de preflight limpia suele incluir:
Access-Control-Allow-OriginAccess-Control-Allow-Methods(incluye el método que usarás, comoGET, POST, PATCH)Access-Control-Allow-Headers(cubre lo que el navegador solicitó)Vary: Origin- Opcional:
Access-Control-Max-Agepara reducir preflights repetidos
Un consejo práctico: asegúrate de que tu middleware de CORS se ejecute antes del enrutamiento, las comprobaciones de auth y los manejadores de error. Si no, las respuestas 401/403/500 pueden saltarse las cabeceras CORS y desencadenar mensajes confusos de “CORS bloqueado” que en realidad son errores de auth o servidor.
Credenciales y comodines: la trampa más común en producción
Muchos errores CORS tras el despliegue vienen de una regla que las personas solo aprenden en producción: no puedes combinar credenciales con un origen comodín.
Si el navegador envía cookies (o le dices que envíe credenciales), el servidor debe responder con un origen específico, no *. Así que esto fallará:
Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true
Cookies vs Authorization: por qué se comportan distinto
Las cookies se adjuntan automáticamente por el navegador, por eso están fuertemente controladas. Si tu frontend y API están en dominios distintos, las cookies no se enviarán a menos que optes por ello en el frontend y lo permitas en el backend.
Una cabecera Authorization: Bearer ... es distinta. No depende de cookies, pero a menudo dispara un preflight porque no es una petición simple. Eso también puede romper, pero la trampa “comodín + credenciales” suele referirse a cookies.
En el frontend, esta línea cambia todo el requisito:
fetch("https://api.example.com/me", {
credentials: "include"
})
Una vez que añades credentials: "include" (o withCredentials: true en Axios), el navegador rechazará respuestas a menos que el backend sea muy explícito.
Qué debe enviar el backend (siempre)
Asegúrate de que tus respuestas API incluyan:
Access-Control-Allow-Origin: https://tu-frontend.com(coincidencia exacta)Access-Control-Allow-Credentials: trueVary: Origin(para que los caches no mezclen orígenes)
Escenario de ejemplo: localmente llamas de http://localhost:3000 a http://localhost:8000 y funciona. En producción pasas a https://app.yourdomain.com y empiezas a usar cookies para login. Si tu servidor sigue respondiendo Access-Control-Allow-Origin: *, el navegador bloqueará la respuesta aunque la API devuelva 200.
Si heredaste código backend generado por IA, esta mala configuración es común. FixMyMess suele encontrar las cabeceras correctas en una ruta, pero faltantes en el refresh de auth o en respuestas de error, lo que hace que el bug parezca aleatorio.
Proxies, CDNs y configuración de plataforma que sobrescriben tu app
Los errores CORS tras el despliegue a menudo aparecen porque tu frontend ya no habla directamente con tu servidor de app. Un reverse proxy, balanceador de carga, CDN o una capa “de seguridad” de la plataforma puede cambiar peticiones y respuestas de formas que tu entorno local nunca hizo.
Cuando el edge cambia tus cabeceras
Muchos proxies pueden añadir, quitar o duplicar cabeceras. Si tu app devuelve el Access-Control-Allow-Origin correcto, pero el proxy lo sobrescribe (o añade un segundo), los navegadores pueden rechazar la respuesta. Otro problema silencioso: el proxy comprime o redirige respuestas de forma distinta, y la respuesta de redirección carece de cabeceras CORS aunque el endpoint final sea correcto.
Un fallo típico solo en producción es que las peticiones OPTIONS nunca lleguen a tu app. Algunas plataformas tratan a OPTIONS como sospechoso, lo bloquean con una regla de firewall o lo enrutan a un manejador por defecto que devuelve 404/405 sin cabeceras CORS. Desde el navegador, parece que “CORS está roto”, pero el problema real es el enrutamiento.
El cache del CDN puede servir cabeceras CORS erróneas
Si un CDN cachea respuestas de API sin variar por Origin, puede servir accidentalmente una respuesta con cabeceras CORS pensadas para otro sitio. Ejemplo: el usuario A golpea tu API desde https://app.example.com y el CDN cachea la respuesta con ese origen permitido. El usuario B pide la misma URL desde https://admin.example.com y recibe las cabeceras cacheadas, que no coinciden, así que el navegador lo bloquea.
Esto es lo que debes verificar, en orden:
- Comprueba las cabeceras de respuesta en el edge (CDN/proxy) y en el servidor de la app, y confirma que coincidan.
- Confirma que las peticiones
OPTIONSestán permitidas y enrutadas a tu app, no bloqueadas o manejadas por una regla por defecto. - Asegura que las redirecciones (http a https, apex a www) devuelvan también cabeceras CORS.
- Si hay cache, asegúrate de que las respuestas varíen por
Origino desactiva el cache para rutas de API. - Compara las reglas de proxy de staging vs producción línea por línea.
Si heredaste un backend generado por IA, este desajuste en la capa de proxy es un culpable frecuente que FixMyMess encuentra durante una auditoría de código, porque la app “parece correcta” pero la configuración del edge discrepa silenciosamente.
Variables de entorno y allowlists de dominio: puntos de ruptura silenciosos
Los errores CORS tras el despliegue a menudo se rastrean a un cambio aburrido: tu frontend ahora llama a una URL distinta de la que crees. Localmente, un servidor dev puede proxyar peticiones y ocultar el problema. En producción, el navegador habla directamente con la API, así que cualquier desajuste aparece de inmediato.
Un fallo común es subir el frontend con la URL base de API equivocada. Por ejemplo, tu build toma API_URL=https://staging-api... (o una URL de preview vieja) porque la variable de entorno de producción nunca se configuró, o el hosting cacheó un build anterior. El navegador entonces envía peticiones desde tu dominio en vivo a una API de staging que no permite ese origen.
Otro punto silencioso es la allowlist del backend. Los equipos añaden http://localhost:3000 durante el desarrollo y se olvidan de añadir el dominio real después. Se complica cuando hay múltiples dominios: www vs sin www, un dominio de marketing, un subdominio de app y un dominio de preview. Si falta aunque sea un origen, ese entorno “fallará” de forma aleatoria.
Para evitar deriva, centraliza tus orígenes permitidos y trátalos como una superficie de configuración real, no como cadenas dispersas.
Una forma práctica de prevenir deriva de configuración
Mantén las reglas en un solo lugar y estrictas:
- Usa una variable de entorno para orígenes permitidos (separados por comas) y procesa esa lista al iniciar.
- Normaliza dominios (incluye el esquema y host exacto que sirves:
https://app.example.com). - Mantén valores separados para dev, staging y producción, y documenta qué URL de frontend mapea a qué API.
- Registra la allowlist resuelta al arrancar (y el Origin de la petición en fallos CORS).
- Añade una prueba de humo rápida después de cada despliegue: una llamada API desde el dominio real.
Si heredaste una app generada por IA, este es un diagnóstico frecuente de FixMyMess: el frontend apunta a un entorno mientras la configuración CORS del backend apunta a otro, y nadie lo nota hasta el primer despliegue real.
Una estrategia repetible para arreglar CORS (paso a paso)
Cuando aparecen errores CORS tras el despliegue, trátalo como una tarea de depuración, no como un juego de adivinanzas. El objetivo es encontrar la petición exacta que el navegador está bloqueando y la cabecera exacta que falta o está mal.
Empieza en la pestaña Network de DevTools. Filtra por la llamada API que falla y busca una petición OPTIONS justo antes. Si ves OPTIONS, tienes un preflight. Si no, el error suele estar en la petición real (a menudo una cabecera faltante en una respuesta 401/500).
Usa esta secuencia y no te saltes pasos:
- Reproduce y captura la petición que falla: copia los detalles de la petición desde la pestaña Network, incluyendo método, URL, código de estado y si hubo preflight OPTIONS.
- Confirma el Origin exacto: lee la cabecera
Originde la petición y anótala exactamente (esquema + dominio + puerto). Muchos problemas de “coincide” son en realidadhttpvshttpsowwwvs sinwww. - Revisa las cabeceras en ambas respuestas: abre la respuesta OPTIONS y la respuesta real. Ambas deben incluir las cabeceras CORS correctas (especialmente
Access-Control-Allow-Origin, y para cookies:Access-Control-Allow-Credentials). - Elimina redirecciones y sorpresas de middleware: si la API redirige (301/302) o fuerza un slash, el preflight suele fallar porque la respuesta redirigida no incluye cabeceras CORS. Haz que OPTIONS devuelva 200/204 directamente con las cabeceras.
- Cambia de
*a una allowlist explícita: configura una lista corta de orígenes permitidos para producción, retesta y luego añade solo lo que realmente necesitas.
Un chequeo rápido: si usas cookies o cabeceras de auth, no puedes usar Access-Control-Allow-Origin: *. Debes reflejar un origen específico y habilitar credenciales.
Si tu backend fue generado por una herramienta IA y la lógica CORS está esparcida por rutas, proxies y ajustes de plataforma, FixMyMess puede localizar la única fuente de verdad y parchearla de forma segura tras una auditoría gratuita de código.
Errores comunes que mantienen CORS roto
Muchos errores CORS tras el despliegue son autoinfligidos. Todo parece bien localmente porque estás en un solo origen, usando peticiones simples y tu servidor dev es permisivo. Producción es más estricto: dominios distintos, HTTPS, cookies y a veces un proxy o CDN delante.
Estos son los errores que más aparecen cuando un frontend habla con un backend separado:
- Permitir cualquier origen en desarrollo y luego activar cookies o auth en producción sin cambiar CORS (no puedes usar
*con credenciales). - Añadir cabeceras CORS sólo en la ruta feliz, pero no en respuestas 401/403/500, así que el navegador oculta el error real tras un mensaje CORS.
- Olvidar que el navegador envía un preflight
OPTIONSpara muchas peticiones, y tu servidor/router/middleware no lo responde con las mismas cabeceras CORS. - Confiar en valores por defecto del framework (o en un snippet de CORS copiado) sin comprobar las cabeceras reales en producción.
- Intentar arreglarlo en el frontend cambiando settings de fetch/axios, cuando CORS lo aplica el navegador y debe arreglarse en el servidor.
Una trampa fácil de pasar por alto: añades Access-Control-Allow-Origin para tu GET /api/me, pero tu capa de auth bloquea la petición temprano. La respuesta 401 no incluye cabeceras CORS, así que el navegador informa un fallo de CORS en lugar de “no autorizado”. Parece que CORS está roto, pero el problema real es “falta la cabecera CORS en errores”.
Otra trampa común es mezclar credenciales y comodines. Si tu frontend usa cookies (o Authorization con withCredentials: true) y tu backend devuelve *, fallará.
Si heredaste código generado por IA, estos problemas suelen estar diseminados entre middleware, funciones serverless y reverse proxies. FixMyMess ve proyectos donde CORS está “configurado” en un lugar pero sobrescrito en otro. El camino más rápido es verificar respuestas reales en producción tanto para OPTIONS como para la petición final, incluyendo casos de error.
Ejemplo: el frontend funciona localmente, falla en el dominio en vivo
Una historia común: construiste una app React en http://localhost:3000 y una API en http://localhost:8080. Inicias sesión, la API pone una cookie y todo funciona.
Luego despliegas. La app React pasa a https://app.yourdomain.com, la API es https://api.yourdomain.com y de repente ves errores CORS tras el despliegue. Lo confuso es que el código no cambió.
Lo que cambió es la regla del navegador. Las cookies cross-site y las comprobaciones de preflight son más estrictas en orígenes HTTPS reales. Localmente quizá no se dispare el preflight, o tu servidor dev esté proxyando peticiones de modo que el navegador nunca vea una llamada cross-origin.
Esto suele arreglarse así para ese setup exacto:
- Establece
Access-Control-Allow-Original origen exacto del frontend (https://app.yourdomain.com), no*. - Establece
Access-Control-Allow-Credentials: truetanto en el preflight (OPTIONS) como en la respuesta real. - Asegúrate de que tu servidor responda a
OPTIONSpara la misma ruta con 200/204 y las mismas cabeceras CORS. - En el frontend, envía cookies intencionalmente (para fetch:
credentials: "include"; para Axios:withCredentials: true). - Asegura que las cookies sean compatibles con peticiones cross-site (a menudo
SameSite=None; Secure).
Cómo confirmar la corrección en producción: abre DevTools Network, encuentra la petición que fallaba y revisa dos entradas. Primero, el preflight OPTIONS debe devolver éxito e incluir allow-origin y allow-credentials. Segundo, la llamada API real debe devolver las mismas cabeceras y realmente establecer o enviar la cookie.
Si un backend generado por IA es inconsistente (OPTIONS manejado por una capa, la petición real por otra), plataformas como FixMyMess suelen encontrar el desajuste rápido durante una auditoría y parchearlo de forma segura.
Lista rápida y siguientes pasos
Cuando veas errores CORS tras el despliegue, trátalo como un problema de coincidencia de cabeceras, no como un misterio. Empieza revisando qué recibió realmente el navegador en la petición que falla y (si ocurre) en el preflight.
Chequeos rápidos que atrapan la mayoría de problemas reales:
- Confirma que el Origin coincide exactamente con lo que el servidor permite (esquema + dominio + puerto).
https://app.comyhttps://www.app.comson orígenes distintos. - Si envías cookies o auth, asegúrate de que las reglas de credenciales sean correctas: la respuesta debe incluir
Access-Control-Allow-Credentials: truey el origen no puede ser*. - Abre el panel de red e inspecciona el preflight OPTIONS: debe devolver un estado de éxito (típicamente 200 o 204), con las mismas cabeceras CORS que la petición real.
- Verifica que
Access-Control-Allow-Headersincluya lo que realmente envías (faltas comunes:Authorization,Content-Type,X-Requested-With). - Asegúrate de que el preflight no sea redireccionado (301/302). Las redirecciones suelen ocurrir tras el despliegue por http a https o por falta de slash final.
Si todo eso parece correcto pero aún falla, el problema suele estar una capa por encima de tu código: un proxy, CDN o configuración de plataforma que reescribe cabeceras, cachea una respuesta vieja o responde OPTIONS de forma distinta que tu app.
Siguientes pasos para que no vuelva a ocurrir:
- Anota los orígenes permitidos por entorno (local, staging, producción) y mantenlos en configuración centralizada en lugar de dispersarlos.
- Añade una simple verificación de integración que corra contra el dominio desplegado y valide las cabeceras de preflight.
- Decide un enfoque de auth (cookies o tokens) y alinea CORS y la configuración de sesión con esa decisión.
- Registra peticiones OPTIONS en producción, al menos temporalmente, para ver códigos de estado y cabeceras.
Si tu app fue generada por herramientas como Lovable, Bolt, v0, Cursor o Replit y la configuración CORS está enmarañada entre código backend y ajustes de despliegue, FixMyMess puede ejecutar una auditoría gratuita de código y alinear las cabeceras del backend y la configuración de la plataforma rápidamente.