CORS: qué es, cómo funciona y se configura

14min

Es uno de los problemas más recurrentes cuando desarrollamos sitios web modernos, que obtienen datos mediante conexiones JavaScript asíncronas que ofrecen las API. Si es tu modelo de trabajo es muy probable que muchas veces hayas lidiado con un error CORS de JavaScript. Te explicamos cómo lo puedes solucionar.

Índice

¿Qué es CORS?

Antes de explicar las soluciones al problema CORS vamos a entender de qué se trata. CORS son las siglas de Cross-Origin Resource Sharing. Se trata de un mecanismo de seguridad implementado en navegadores web. Este mecanismo sirve para controlar las solicitudes HTTP realizadas desde Javascript con un origen diferente al del servidor (generalmente cuando el dominio de origen difiere del dominio de destino de la solicitud).

Gracias a CORS, los servidores pueden especificar quién puede acceder a sus recursos y qué métodos HTTP pueden ser utilizados, lo que evita que los servicios web se usen desde lugares que no están autorizados.

Cómo funciona CORS en la web

Ahora que ya sabes qué es CORS y antes de pasar a ver cómo solucionar posibles problemas, vamos a realizar una descripción paso por paso del funcionamiento de este mecanismo.

1. Comprende la política de mismo origen

La política de «mismo origen» es una medida de seguridad que restringe cómo los documentos y scripts pueden ser cargados, dependiendo del origen.

Primero debes entender que CORS permite que los contenidos sean accesibles desde el mismo origen. Por ejemplo, una página web que reside en https://example.com podrá consultar un recurso alojado en https://example.com/api/customers. Sin embargo, de manera predeterminada no podrá hacer solicitudes a https://api.customers-en-otro-dominio.com.

2. Identifica las necesidades de Cross-Origin Requests

En los casos en los que sí se desee habilitar las consultas a recursos del servidor desde otros dominios es necesario definir cabeceras adecuadas en las respuestas. Este tipo de solicitudes Cross-Origin son comunes en aplicaciones modernas que consumen API de terceros.

3. Configura el servidor para permitir CORS

Para permitir CORS, de modo que se puedan realizar solicitudes desde otros dominios, es necesario que el servidor se configure correctamente para incluir las cabeceras HTTP de control de acceso en sus respuestas. 

4. Añade cabeceras de control de acceso

Por tanto, la solución para todos los problemas de CORS consiste en asegurarse que el servidor incluye las cabeceras adecuadas en las respuestas. Aquí tenemos la descripción de las cabeceras más comunes para CORS:

  • Access-Control-Allow-Origin: Especifica qué orígenes pueden acceder a los recursos del servidor del API.
  • Access-Control-Allow-Methods: Indica los métodos HTTP permitidos en ese acceso, ya que no todos los métodos pueden ser válidos.
  • Access-Control-Allow-Headers: En este caso se informa de la lista de cabeceras permitidas.
  • Access-Control-Allow-Credentials: Con este valor se indica si las credenciales (cookies, cabeceras de autenticación) pueden ser enviadas.

Más tarde veremos cómo gestionar estas cabeceras en diversos stacks de tecnologías. Muchas veces se tratará simplemente de realizar una configuración del sistema backend.

5. Gestiona solicitudes simples y Preflight Requests

Las solicitudes simples son aquellas que usan métodos HTTP seguros como GET o POST. Estas solicitudes no requieren una verificación previa. 

Las Preflight Requests, sin embargo, son solicitudes OPTIONS enviadas por el navegador para verificar si el servidor permite una solicitud compleja (como es el caso de los métodos PUT, PATCH o DELETE). Más adelante en este artículo te explicaremos cómo gestionar estas solicitudes.

6. Verifica la seguridad y las excepciones

Para mantener la seguridad del servidor y los datos de los servicios web es importante verificar la configuración de CORS, de modo que nos aseguremos de que solo se ofrecen aquellos servicios absolutamente imprescindibles desde otros orígenes.

7. Prueba y valida la configuración de CORS

Puedes usar herramientas como Postman, una de las más conocidas a la hora de consumir API en tiempo de desarrollo, para probar las configuraciones CORS antes de usarlas desde otros dominios con JavaScript, que suele ser mucho más laborioso.  Además, antes de llevar a producción te recomendamos probar el sistema en un entorno de preproducción, porque a veces las restricciones de CORS aparecen cuando estás usando dominios reales en tu proyecto y no dominios de desarrollo como localhost.

Configuración básica de CORS

La configuración de CORS depende mucho del stack de tecnologías que estés utilizando, como el servidor web, lenguaje de programación o frameworks. Vamos a ver a continuación unas guías básicas que tratan de establecer los mecanismos más comunes en configuraciones habituales.

Introducción a la configuración de CORS

Configurar CORS consiste en ajustar las cabeceras HTTP en las respuestas del servidor. Esto se puede realizar a varios niveles que vamos a resumir en los siguientes apartados.

Cabeceras HTTP para permitir CORS

El paso fundamental consiste en agregar las cabeceras necesarias en las respuestas HTTP. Por supuesto, esta respuesta HTTP la tiene que producir el servidor web, por lo que requiere cierta configuración. No obstante, os dejamos un resumen del resultado que debes conseguir, aunque luego explicaremos cómo producirlo en diferentes casos.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type

Configuración de CORS en el servidor Apache

Si estás trabajando con Apache, se pueden configurar las cabeceras CORS usando el archivo .htaccess. Por supuesto, también podrías usar el archivo global de configuración del servidor, aunque es menos frecuente.

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin 'https://example.com'
    Header set Access-Control-Allow-Methods 'GET, POST, OPTIONS'
    Header set Access-Control-Allow-Headers 'Content-Type'
</IfModule>

Configuración de CORS en el servidor Nginx

En Nginx, la configuración de las cabeceras necesarias para CORS se realiza de manera distinta. Lo harás en el bloque server o location del archivo de configuración del servidor:

server {
    location / {
        add_header Access-Control-Allow-Origin 'https://example.com';
        add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'Content-Type';
    }
}

Sin embargo, lo cierto es que generalmente los desarrolladores acaban configurando CORS mediante el propio software desarrollado en los lenguajes de programación que estén utilizando. Vamos a ver algunas configuraciones en los lenguajes más comunes para el desarrollo web.

Configuración de CORS en Node.js

En Node.js generalmente usaremos el framework Express. Aunque existen otros es el más extendido. Con Express podemos usar los middlewares y el package cors de npm. Si queremos activar CORS podemos usar un código como este:

const express = require('express');
const cors = require('cors');
const app = express();
const corsOptions = {
  origin: 'https://example.com',
  methods: 'GET,POST,PUT,DELETE',
  allowedHeaders: 'Content-Type',
};
app.use(cors(corsOptions));
app.listen(3000, () => {
  console.log('Servidor iniciado en el puerto 3000');
});

Configuración de CORS en un entorno de API REST

Si estamos desarrollando un API REST lo más recomendable es utilizar un framework que nos permita resumir las tareas de desarrollo y configuración necesarias para este tipo de servicios. Existen multitud de frameworks recomendables para solventar esta tarea casi de manera inmediata. Algunos ejemplos son Laravel, Symfony, Django, Flask, o Spring Boot. Cada uno dispone de configuraciones específicas para gestionar CORS. Habitualmente solamente tendrás que indicar los patrones de las URLs que deben enviar las cabeceras de CORS y el propio framework se encargará de producirlas automáticamente para los recursos localizados en esas rutas.

Configuración avanzada de CORS 

En la mayor parte de los casos, que hemos mencionado hasta ahora, podrás resolver los problemas de CORS de tus proyectos. Para casos más complejos vamos a ofrecer algunas guías para configuración avanzada de CORS.

Manejo de solicitudes Preflight (Preflight Requests)

Las solicitudes Preflight son enviadas por el navegador automáticamente. De hecho, podremos verlas en la consola de desarrolladores y las identificaremos fácilmente porque utilizan el método HTTP OPTIONS.

Estas solicitudes suelen producirse antes de hacer una solicitud compleja (como PUT, DELETE, o cualquier solicitud con cabeceras no estándar). El navegador la hace para verificar si el servidor permite la solicitud con los métodos y cabeceras especificados. El servidor generalmente gestiona solicitudes Preflight y los frameworks son capaces de producir las respuestas adecuadas. Si no es el caso, te pasamos las cabeceras necesarias para responder este tipo de solicitudes sin problemas de CORS.

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Control de acceso granular con CORS

En muchas ocasiones no deseas activar CORS para todo un dominio, sino que se prefiere especificar la validez de las solicitudes por origen cruzado dependiendo del tipo de recursos a los que se está intentando acceder. Nuevamente, si estamos utilizando un framework es muy probable que te permita una configuración granular, es decir, una configuración específica dependiendo de las rutas y/o los dominios de origen. Generalmente es tan sencillo como rellenar un array con valores. 

Si no usas frameworks, es probable que necesites configuraciones un poquito más complejas en los servidores web. Te damos una plantilla que te puede servir de referencia, aunque aquí necesitarás definir muy bien qué se puede y qué no se puede acceder atendiendo a la estructura de tu servicio web.

Esta sería la configuración habitual en el servidor Nginx, que es el más recurrente cuando se trata de servir un API.

location /api/recurso_publico {
    add_header Access-Control-Allow-Origin '*';
}
location /api/recurso_granular {
    add_header Access-Control-Allow-Origin 'https://domini-origen.com';
    add_header Access-Control-Allow-Methods 'GET, POST';
}

```

Uso de credenciales en solicitudes CORS

Otro asunto que puede causar complicaciones extra es la gestión de las credenciales en las solicitudes CORS. Las credenciales de acceso pueden llegar por medios como cookies, en proyectos SPA stateful, o cabeceras de autenticación cuando se trata de API Stateless. En ambos casos se controla con la cabecera Access-Control-Allow-Credentials.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

Pero en este caso la conexión no implica sólo la respuesta del servidor. También tienes que usar programación JavaScript del lado del cliente para incluir cabeceras en la solicitud. Lo frecuente es enviar la opción withCredentials desde JavaScript:

Te damos un ejemplo de uso de fetch para el acceso Ajax que incluye las cabeceras con las credenciales.

fetch('https://api.example.com/data', {
  method: 'GET',
  credentials: 'include',
});

Además incluye otros valores pueden ser omit o same-origin, que es el predeterminado.

Restricción de métodos HTTP permitidos

Para mantener lo más seguro nuestro servicio web es importante restringir aquellos métodos HTTP en los que queremos permitir accesos por origen cruzado. Esta sería una configuración de Nginx para conseguir especificar solamente los métodos GET y POST.

add_header Access-Control-Allow-Methods 'GET, POST';

Gestión de cabeceras expuestas

La configuración Access-Control-Expose-Headers es importante para permitir que el navegador acceda a ciertas cabeceras en la respuesta del servidor. Son cabeceras HTTP, igual que siempre, que deben tener esta forma:

HTTP/1.1 200 OK
Access-Control-Expose-Headers: X-Custom-Header, Content-Length

Depuración y solución de problemas avanzados de CORS

Depurar problemas de CORS puede ser un dolor de cabeza. Por ello queremos darte algunas recomendaciones para solventar posibles problemas:

  • Revisa las respuestas del servidor y las cabeceras CORS. Esto es posible de realizar con las herramientas de desarrollo del navegador.
  • Utiliza herramientas de conexión con los servicios web o API REST del estilo de Postman, ya que están pensadas para facilitar la vida del desarrollador.
  • Si tienes problemas recurrentes verificar configuraciones de proxy y CDN que puedan afectar a las cabeceras.

Problemas comunes con CORS y soluciones

Ahora vamos a ver algunos problemas específicos que tienen que ver con CORS y la solución que puedes aplicar para resolverlos.

Error de acceso denegado: solución y causas

El error de acceso denegado es el más frecuente cuando estamos lidiando con issues relacionadas con CORS. Ocurre cuando el origen de la solicitud no está permitido por el servidor web del API. Esto puede deberse a motivos como:

  • Falta de la cabecera Access-Control-Allow-Origin en la respuesta del servidor
  • Configuración incorrecta de los orígenes permitidos, en el caso que estemos configurando CORS de manera granular.

Para solucionarlo debes asegurarte que la cabecera Access-Control-Allow-Origin está presente y que tiene habilitado el origen correcto. * indica que se permite cualquier origen.

Problemas con solicitudes Preflight y cómo resolverlos

Los problemas con las solicitudes Preflight también pueden ser un problema frecuente cuando no usamos frameworks. Suelen deberse a la falta de respuestas adecuadas a las solicitudes OPTIONS. Recuerda que deben indicarse las cabeceras de respuesta que se señalaron en un punto anterior.

Manejo de credenciales y autenticación en CORS

Existen diversos métodos de autenticación en los servicios web. Dependiendo del que estés utilizando puede que necesites configuraciones específicas. En los casos más elementales te recomendamos asegurarte que Access-Control-Allow-Credentials y que Access-Control-Allow-Origin no sea *, ya que si Access-Control-Allow-Credentials está configurado en true, la especificación de CORS prohíbe el uso de * por razones de seguridad.

Configuración incorrecta de cabeceras CORS: diagnóstico y arreglo

Para diagnosticar un problema de configuración de cabeceras CORS te recomendamos analizar las cabeceras en las herramientas de desarrollo del navegador. Casi todos los navegadores ofrecen unas herramientas de desarrollo similares, aunque a a veces hay algunas diferencias entre ellos. Si no lo ves claro con algún navegador en particular, prueba cómo otro navegador te las entrega porque igual te resulta más fácil de identificar los problemas. 

Soluciones para restricciones de dominio en CORS 

En el caso que necesites permitir múltiples orígenes en tu API REST puede ser recomendable listar específicamente cada origen, para aumentar la seguridad del servidor. Puedes también saltarte esta recomendación y * para poder acceder desde cualquier origen y comprobar si con esta configuración está permitido el acceso sin los problemas CORS que estás pudiendo recibir.

Problemas de compatibilidad entre navegadores y CORS

Si tienes problemas CORS, verifica las conexiones desde varios navegadores. Así te asegurarás que los problemas recibidos no sean propios de un cliente web que este poco actualizado. Generalmente, los navegadores modernos se actualizan ellos mismos de manera automática, por lo que no debería haber problema en este sentido. Puedes además utilizar herramientas como Postman para verificar también los accesos con ellas, ya general estas herramientas tienen todas las cabeceras HTTP de origen configuradas correctamente.

Estrategias para depurar y solucionar problemas de CORS en producción

Cuando utilizamos localhost los navegadores son más permisivos en el acceso a recursos de origen cruzado. Es por ello que muchas veces cuando publicamos el proyecto en producción aparecen los problemas de CORS. En estos casos no debes preocuparte porque los problemas se resolverán con los puntos que hemos mencionado anteriormente. No obstante, puedes seguir estos consejos:

  • Verifica el registro de logs del servidor para saber si hay algún error en el backend.
  • Verifica el origen desde otros dominios para saber si el dominio al que estás accediendo es permitido, incluso subdominios dentro del mismo dominio pueden dar problemas.
  • Utiliza las herramientas de desarrollo del navegador para inspeccionar las cabeceras de las solicitudes.
  • Verifica que estás enviando correctamente las cabeceras Ajax para indicar que esperas respuestas en JSON.
  • Verifica que el servidor no esté entregando otros errores en las solicitudes como 404 y devolviendo páginas de error predeterminadas con contenido HTML.
  • Verifica las configuraciones de CORS de tu framework y repasa la documentación.
  • Utiliza sandboxes en tu mismo dominio para descartar que el problema no sea de programación del propio API antes que el propio CORS.
Fernán García de Zúñiga

Productos relacionados: