Qué es CORS y cómo funciona en el navegador
Autor: Redacción Domeny.ForSale Publicado
En pocas palabras: CORS (Cross-Origin Resource Sharing) es un mecanismo que permite al servidor autorizar al navegador a compartir una respuesta con un script que se ejecuta en otro origen. Las cabeceras de respuesta del servidor establecen los permisos y el navegador se encarga de aplicarlos. Por tanto, un error de CORS suele indicar un problema de permisos para leer la respuesta, no necesariamente una falta de conexión con el servidor.
Por qué el navegador limita el acceso entre orígenes
Los navegadores aplican la política del mismo origen, o same-origin policy. Esta limita la capacidad de un script para leer datos de otro origen. Así, una página abierta en una pestaña no puede obtener y leer libremente datos privados de otros servicios en los que el usuario haya iniciado sesión.
El origen (origin) es la combinación de tres elementos: protocolo, nombre de host y puerto. Basta con que cambie uno de ellos para que el origen sea distinto. La ruta que aparece después del nombre de host no influye en esta comparación.
Para la página https://tienda.example, la comparación es la siguiente:
| Dirección de destino | ¿Mismo origen? | Motivo |
|---|---|---|
https://tienda.example/productos | Sí | Solo cambia la ruta |
https://api.tienda.example | No | El nombre de host es distinto |
http://tienda.example | No | El protocolo es distinto |
https://sklep.example:8443 | No | El puerto es distinto del predeterminado, 443 |
CORS permite al servidor flexibilizar esta política de forma controlada. No es un método HTTP, como GET o POST, sino un mecanismo basado en cabeceras HTTP.
Un ejemplo sencillo: una tienda obtiene una lista de productos
Supongamos que la página está en https://tienda.example y que la API, es decir, la interfaz que proporciona los datos de la aplicación, está en https://api.tienda.example. El script de la página quiere obtener los productos:
fetch('https://api.sklep.example/produkty')
.then(respuesta => respuesta.json())
.then(productos => console.log(productos));
Como las direcciones tienen orígenes distintos, el navegador añade a la petición una cabecera que indica el origen de la página:
Origin: https://sklep.example
El servidor de la API puede responder con los datos y una cabecera que autorice el acceso:
Access-Control-Allow-Origin: https://sklep.example
El navegador compara ese valor con el origen de la página. Si la autorización coincide, permite que el script acceda a la respuesta. Si falta la cabecera o indica otra dirección, el script no puede leer la respuesta, aunque el servidor haya devuelto el estado 200 OK.
La cabecera de autorización debe proceder del servidor de la API. Añadir Access-Control-Allow-Origin al código de la petición en el navegador no resuelve el problema.
Cuándo envía el navegador primero una petición OPTIONS
No todas las peticiones entre orígenes distintos se envían directamente. En algunos casos, el navegador realiza primero una preflight, una petición preliminar que comprueba si el servidor autoriza el acceso.
Por ejemplo, enviar un pedido con el método POST y el tipo de contenido application/json activa este paso previo. Lo mismo ocurre al utilizar la cabecera Authorization. La petición preliminar puede tener este aspecto:
OPTIONS /pedidos HTTP/1.1
Origin: https://sklep.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
El servidor debe responder con una autorización que incluya el origen, el método y las cabeceras necesarias:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type
Solo cuando la comprobación se completa correctamente, el navegador envía la petición POST propiamente dicha. La respuesta a esa petición POST también debe incluir la cabecera Access-Control-Allow-Origin adecuada.
No todas las peticiones POST requieren una preflight. También influyen las cabeceras y el tipo de contenido enviado; una petición GET normal, sin cabeceras adicionales, a menudo no necesita una petición preliminar.
Cookies y asterisco: dos errores importantes que evitar
Una API pública cuyas respuestas deban ser accesibles para scripts de cualquier origen puede devolver:
Access-Control-Allow-Origin: *
Sin embargo, el asterisco no es una solución válida para las peticiones que envían cookies en un modo que incluye credenciales. En ese caso, el código puede usar credentials: 'include', y el servidor debe indicar un origen permitido concreto y devolver:
Access-Control-Allow-Origin: https://sklep.example
Access-Control-Allow-Credentials: true
La configuración de CORS por sí sola no garantiza el envío de cookies. También se aplican las reglas de las cookies, entre ellas SameSite, y las restricciones de privacidad del navegador.
Si el servidor admite varios orígenes de confianza, debe comprobar la cabecera Origin con una lista de valores permitidos. Devolver sin comprobarlo cualquier origen recibido y autorizar el uso de credenciales puede exponer respuestas privadas. Cuando la respuesta depende de Origin, también hay que incluir Vary: Origin para que la caché distinga las distintas variantes de la respuesta.
Cómo diagnosticar un error de CORS
Abre la pestaña de red de las herramientas de desarrollo del navegador y realiza estas cuatro comprobaciones:
- Compara el protocolo, el host y el puerto de la página con los de la API.
- Comprueba si hay una petición
OPTIONSy si el servidor responde correctamente a ella. - Revisa las cabeceras de respuesta de la petición propiamente dicha, también cuando el servidor devuelve un error.
- Comprueba que la autorización incluya el método, las cabeceras y, si corresponde, las credenciales utilizadas.
Que la llamada funcione en curl no demuestra que CORS esté bien configurado: esta herramienta no aplica las restricciones del navegador. Usar mode: 'no-cors' tampoco permite leer el contenido JSON: produce una respuesta opaca cuyo contenido no es accesible para el script.
CORS no sustituye al inicio de sesión, al control de permisos ni a la protección contra CSRF, que consiste en provocar acciones no deseadas desde otra página. Algunas peticiones pueden llegar al servidor aunque se bloquee la lectura de la respuesta. Para resolver el problema, empieza por la configuración de la API o de un intermediario bajo tu control, no por desactivar las protecciones del navegador.
Consulta también: ¿Qué es una API?
