¿Qué es el Error CORS y por qué bloquea tus peticiones HTTP?
El Intercambio de Recursos de Origen Cruzado (CORS, por sus siglas en inglés, Cross-Origin Resource Sharing) es un mecanismo de seguridad implementado por los navegadores web modernos. Su propósito fundamental es proteger a los usuarios de ataques maliciosos, impidiendo que un sitio web cargado en el navegador realice peticiones HTTP a un dominio diferente del que originó la página, a menos que el servidor de destino autorice explícitamente dicha comunicación.
Cuando desarrollas aplicaciones web con JavaScript (utilizando Fetch API, Axios o AJAX tradicional), es sumamente común encontrarse con el temido mensaje en la consola del navegador: Access to fetch at 'https://api.ejemplo.com/datos' from origin 'http://localhost:3000' has been blocked by CORS policy. Este error no es un fallo de tu código JavaScript, sino una medida de seguridad del navegador que bloquea la respuesta porque el servidor de destino no ha enviado las cabeceras HTTP correctas que permitan el acceso desde tu origen.
La Política de Mismo Origen (Same-Origin Policy)
Para entender CORS, primero debemos comprender la Política de Mismo Origen (SOP). Dos URLs comparten el mismo origen si y solo si coinciden exactamente en tres elementos: el protocolo (por ejemplo, http vs https), el host o dominio (ejemplo.com vs api.ejemplo.com) y el puerto (por ejemplo, :80 vs :3000). Si cualquiera de estos tres elementos varía, el navegador considerará la petición como de origen cruzado (Cross-Origin) y aplicará las restricciones de seguridad pertinentes.
Peticiones Preflight (Pre-vuelo) y el método OPTIONS
En peticiones complejas (aquellas que no son GET, HEAD o POST simples, o que incluyen cabeceras personalizadas como Authorization o tipos de contenido como application/json), el navegador no envía la petición real de inmediato. En su lugar, realiza una petición previa de control llamada Preflight Request utilizando el método HTTP OPTIONS. El navegador pregunta al servidor: '¿Tienes permitido recibir una petición de este origen con estas cabeceras y este método?'. Si el servidor responde afirmativamente con los headers adecuados, el navegador procede a enviar la petición real; de lo contrario, la bloquea de inmediato.
Método 1: Configurar CORS en el Backend (La Solución Correcta)
La única solución definitiva y segura para resolver los problemas de CORS en producción consiste en configurar el servidor que aloja la API para que devuelva las cabeceras HTTP requeridas. A continuación, veremos cómo implementarlo en los entornos de backend más populares.
Solución en Node.js con Express
En aplicaciones construidas con Node.js y el framework Express, la forma más rápida y segura de gestionar CORS es utilizando el middleware oficial de terceros. Primero, debes instalar el paquete ejecutando en tu terminal:
npm install cors
Una vez instalado, puedes configurarlo en tu archivo principal de servidor. Es altamente recomendable evitar el uso del comodín '*' en entornos de producción, limitando el acceso únicamente a tus dominios de confianza:
const express = require('express');
const cors = require('cors');
const app = express();
const opcionesCors = {
origin: 'https://mi-aplicacion-cliente.com',
methods: 'GET,POST,PUT,DELETE',
allowedHeaders: 'Content-Type,Authorization',
optionsSuccessStatus: 200
};
app.use(cors(opcionesCors));
app.get('/api/datos', (req, res) => {
res.json({ mensaje: 'Acceso autorizado con CORS configurado correctamente' });
});
app.listen(3000);
Método 2: Configuración en Servidores Web (Nginx y Apache)
Si tu API se ejecuta detrás de un servidor web o un proxy inverso, puedes inyectar las cabeceras CORS directamente a nivel de infraestructura. Esto es muy eficiente porque libera a tu aplicación backend de gestionar estas peticiones pre-vuelo.
Configuración en Nginx
Para habilitar CORS en Nginx, debes editar el bloque de configuración del servidor correspondiente a tu API (habitualmente ubicado en /etc/nginx/sites-available/default) e incluir las siguientes directivas dentro del bloque location:
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://mi-aplicacion-cliente.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://mi-aplicacion-cliente.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
}
Configuración en Apache (.htaccess)
Si estás utilizando un servidor web Apache, puedes habilitar el módulo de cabeceras (mod_headers) y añadir las siguientes reglas en tu archivo de configuración principal o directamente dentro del archivo .htaccess de tu directorio raíz:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://mi-aplicacion-cliente.com"
Header set Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE"
Header set Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
</IfModule>
Método 3: Configurar un Proxy en Entornos de Desarrollo (Vite, Webpack o Next.js)
Cuando estás programando de forma local, levantar configuraciones de producción puede ser un proceso lento y tedioso. Si tu aplicación frontend se ejecuta en un servidor de desarrollo como Vite o Webpack, puedes configurar un proxy inverso local para sortear las restricciones de CORS.
El truco consiste en hacer que tu código JavaScript envíe las peticiones al propio servidor de desarrollo local (por ejemplo, http://localhost:5173/api). El servidor de desarrollo, que al no ser un navegador web no está sujeto a las restricciones de CORS, reenvía la petición al backend real y te devuelve la respuesta de forma transparente.
Configuración de Proxy en Vite (vite.config.js)
Abre el archivo de configuración de tu proyecto de Vite y añade la propiedad server.proxy de la siguiente manera:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'https://api-real-externa.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
}
});
Con esta configuración, cualquier llamada que hagas en tu código JavaScript hacia /api/usuarios será redirigida automáticamente por Vite hacia https://api-real-externa.com/usuarios sin generar ningún tipo de error de CORS en el navegador.
Cabeceras CORS Clave que Debes Conocer
Para realizar diagnósticos precisos y solucionar problemas complejos de comunicación entre cliente y servidor, es vital comprender qué hace cada una de las cabeceras HTTP asociadas a CORS:
- Access-Control-Allow-Origin: Especifica qué orígenes tienen permitido acceder al recurso. Puede ser un dominio único, una lista de dominios procesados dinámicamente por el servidor, o el comodín '*' (no recomendado para datos privados o autenticados).
- Access-Control-Allow-Methods: Define qué métodos de petición (GET, POST, PUT, DELETE, etc.) están autorizados al acceder al recurso en respuesta a una petición preflight.
- Access-Control-Allow-Headers: Indica qué cabeceras HTTP personalizadas se pueden utilizar durante la petición real (por ejemplo,
Authorization,X-Api-Key). - Access-Control-Allow-Credentials: Indica si la petición real puede incluir credenciales como cookies de sesión, cabeceras de autorización HTTP o certificados SSL de cliente. Si este header se establece en
true, la cabeceraAccess-Control-Allow-Originno puede ser un asterisco '*'; debe ser obligatoriamente un dominio específico.
Buenas Prácticas de Seguridad con CORS
Habilitar CORS de manera incorrecta puede introducir graves vulnerabilidades de seguridad en tu infraestructura, como ataques de falsificación de petición en sitios cruzados (CSRF) o fugas de datos confidenciales. Sigue siempre estas pautas profesionales:
- Nunca utilices el comodín '*' en producción si manejas datos sensibles: Si tu API requiere autenticación mediante tokens o cookies, configurar el origen en '*' permitirá que cualquier sitio malicioso realice peticiones en nombre de tus usuarios.
- Valida dinámicamente el origen: Si tu arquitectura requiere dar soporte a múltiples dominios clientes, implementa una lista blanca (whitelist) en tu backend que lea la cabecera
Originde la petición entrante y la devuelva de forma dinámica enAccess-Control-Allow-Originúnicamente si coincide con uno de tus dominios aprobados. - Aprovecha el almacenamiento en caché de Preflight: Utiliza la cabecera
Access-Control-Max-Agepara indicar al navegador durante cuántos segundos puede almacenar en caché el resultado de la petición preflight de tipo OPTIONS. Esto reducirá drásticamente la latencia de tu aplicación al evitar peticiones repetitivas innecesarias.