Solución Error CORS Access-Control-Allow-Origin: Guía Definitiva
El error Access to XMLHttpRequest at 'https://api.ejemplo.com' from origin 'https://app.ejemplo.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource es un mecanismo de seguridad implementado por los navegadores web para evitar que scripts maliciosos de un dominio lean datos protegidos de otro origen distinto (mismo protocolo, dominio o puerto).
El Diagnóstico Rápido
El error Access to XMLHttpRequest at 'https://api.ejemplo.com' from origin 'https://app.ejemplo.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource es un mecanismo de seguridad implementado por los navegadores web para evitar que scripts maliciosos de un dominio lean datos protegidos de otro origen distinto (mismo protocolo, dominio o puerto).
La Solución Paso a Paso
-
1
Paso 1: Configurar CORS en Node.js con Express
Si tu backend está construido con Express, utiliza el middleware oficial
cors:JAVASCRIPTimport express from 'express'; import cors from 'cors'; const app = express(); // Lista de dominios autorizados const allowedOrigins = [ 'https://app.tudominio.com', 'http://localhost:3000' ]; app.use(cors({ origin: function(origin, callback) { if (!origin || allowedOrigins.indexOf(origin) !== -1) { callback(null, true); } else { callback(new Error('Bloqueado por política CORS')); } }, credentials: true, // Permitir envío de cookies y tokens Authorization methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); -
2
Paso 2: Solución en Nginx como Proxy Inverso
Si gestionas tus APIs detrás de un servidor web Nginx, puedes inyectar las cabeceras directamente:
NGINXlocation /api/ { if ($request_method = 'OPTIONS') { add_header 'Access-Control-Allow-Origin' 'https://app.tudominio.com' always; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Content-Length' 0; add_header 'Content-Type' 'text/plain charset=UTF-8'; return 204; } add_header 'Access-Control-Allow-Origin' 'https://app.tudominio.com' always; add_header 'Access-Control-Allow-Credentials' 'true' always; proxy_pass http://localhost:5000; } -
3
Paso 3: Solución en Python (FastAPI / Django)
En frameworks modernos de Python:
PYTHON# En FastAPI: from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://app.tudominio.com", "http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) -
4
Paso 4: Validar las cabeceras con cURL
Comprueba que tu servidor responde adecuadamente a una solicitud preflight de prueba:
BASHcurl -I -X OPTIONS https://api.tudominio.com/datos \ -H "Origin: https://app.tudominio.com" \ -H "Access-Control-Request-Method: POST"
❓ Preguntas Frecuentes (FAQ)
¿Por qué Postman o cURL funcionan pero mi navegador falla?
Porque Postman y cURL son clientes de terminal y no ejecutan motores de navegador web; por lo tanto, no aplican la política de seguridad del mismo origen (Same-Origin Policy).
¿Qué es una petición preflight (OPTIONS)?
Es una consulta preliminar que el navegador envía de forma automática antes de peticiones complejas (como aquellas con JSON o cabeceras personalizadas) para verificar si el servidor admite la operación.
Consejo de Prevención
Prácticas de seguridad recomendadas:
- No utilices comodines (*) si envías credenciales: Si configuras
Access-Control-Allow-Origin: *y tu frontend envía cookies o cabeceras de autorización (credentials: 'include'), el navegador rechazará la conexión por motivos de seguridad. - Configura CORS en el backend, no en el frontend: CORS es una restricción impuesta por el navegador que solo el servidor de destino puede autorizar.