
¿Qué es CORS?
CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad implementado en los navegadores web que controla qué sitios web pueden realizar peticiones HTTP a un dominio diferente al suyo. Cuando tu frontend en https://mi-app.com intenta llamar a tu API en https://api.mi-empresa.com, el navegador comprueba si la API permite este acceso antes de realizar la petición real.
Esta política de seguridad se llama Same-Origin Policy y es una protección fundamental del navegador. Sin embargo, en el desarrollo moderno, es muy habitual que el frontend y el backend vivan en dominios o puertos distintos.
¿Cuándo aparece el error de CORS?
El error de CORS aparece en el navegador cuando:
- Tu frontend en
http://localhost:3000llama a tu API enhttp://localhost:8000 - Tu app de React/Vue/Angular en
https://miapp.comllama a una API enhttps://api.miapp.com - Un cliente externo intenta acceder a tu API desde un dominio no autorizado
El mensaje de error en la consola del navegador suele ser:
Access to fetch at 'http://localhost:8000/api/datos' from origin
'http://localhost:3000' has been blocked by CORS policy.
Las peticiones preflight
Antes de enviar ciertos tipos de peticiones (POST, PUT, DELETE, o peticiones con cabeceras personalizadas), el navegador envía una petición preflight OPTIONS para preguntar a la API si permite ese tipo de petición. La API debe responder con los headers CORS adecuados.
CORSMiddleware en FastAPI
FastAPI incluye CORSMiddleware de Starlette para gestionar CORS de forma declarativa. Se añade con app.add_middleware():
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/datos")
async def obtener_datos():
return {"datos": "accesibles desde el frontend"}
Parámetros principales de CORSMiddleware
| Parámetro | Tipo | Descripción |
|---|---|---|
| allow_origins | list[str] | Lista de orígenes permitidos |
| allow_origin_regex | str | Patrón regex para orígenes permitidos |
| allow_methods | list[str] | Métodos HTTP permitidos |
| allow_headers | list[str] | Cabeceras HTTP permitidas |
| allow_credentials | bool | Permite cookies y cabeceras Authorization |
| expose_headers | list[str] | Cabeceras visibles para el navegador |
| max_age | int | Segundos que el navegador cachea el preflight |
Configuración para desarrollo
Durante el desarrollo local, es habitual que el frontend corra en un puerto distinto al backend:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# Orígenes típicos en desarrollo local
origenes_desarrollo = [
"http://localhost:3000", # React por defecto
"http://localhost:4200", # Angular por defecto
"http://localhost:5173", # Vite por defecto
"http://127.0.0.1:3000",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origenes_desarrollo,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allow_headers=["Authorization", "Content-Type", "X-Requested-With"],
)
Configuración con comodín para desarrollo rápido
Si quieres permitir cualquier origen durante el desarrollo (útil para pruebas rápidas):
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Permitir cualquier origen
allow_methods=["*"], # Permitir cualquier método HTTP
allow_headers=["*"], # Permitir cualquier cabecera
)
Importante: El comodín "*" no puede combinarse con allow_credentials=True. Si necesitas credenciales, debes especificar los orígenes explícitamente.
Configuración para producción
En producción, la configuración debe ser más restrictiva para garantizar la seguridad:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic_settings import BaseSettings
class Configuracion(BaseSettings):
allowed_origins: list[str] = ["https://miapp.com"]
debug: bool = False
class Config:
env_file = ".env"
config = Configuracion()
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=config.allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
max_age=3600, # El navegador cachea el preflight 1 hora
)
En el archivo .env de producción:
ALLOWED_ORIGINS=["https://miapp.com","https://www.miapp.com"]
CORS con credenciales (cookies y Authorization)
Cuando tu frontend envía cookies o la cabecera Authorization en sus peticiones, necesitas habilitar allow_credentials=True:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
# Con credentials, los orígenes DEBEN ser explícitos (no "*")
allow_origins=[
"https://miapp.com",
"https://admin.miapp.com",
],
allow_credentials=True, # Permite enviar cookies y Authorization
allow_methods=["*"],
allow_headers=["*"],
)
Y en el frontend (JavaScript/TypeScript), debes incluir credentials: "include" en las peticiones:
const respuesta = await fetch("https://api.miapp.com/usuarios", {
method: "GET",
credentials: "include", // Envía las cookies con la petición
headers: {
"Authorization": `Bearer ${token}`
}
});
CORS con regex para subdominios
Si tienes múltiples subdominios que deben acceder a tu API, puedes usar una expresión regular:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
# Permite cualquier subdominio de miapp.com
allow_origin_regex=r"https://.*\.miapp\.com",
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Esto permitirá peticiones desde https://app.miapp.com, https://admin.miapp.com, https://tienda.miapp.com, etc.
Configuración dinámica según el entorno
Un patrón habitual es diferenciar la configuración CORS según el entorno de ejecución:
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
ENTORNO = os.getenv("ENTORNO", "desarrollo")
if ENTORNO == "produccion":
origenes = [
"https://miapp.com",
"https://www.miapp.com",
]
allow_all = False
elif ENTORNO == "staging":
origenes = [
"https://staging.miapp.com",
"https://preview.miapp.com",
]
allow_all = False
else:
# Desarrollo: orígenes locales
origenes = [
"http://localhost:3000",
"http://localhost:5173",
"http://127.0.0.1:3000",
]
allow_all = True
app.add_middleware(
CORSMiddleware,
allow_origins=origenes,
allow_credentials=not allow_all,
allow_methods=["*"],
allow_headers=["*"],
)
Verificar que CORS funciona correctamente
Para comprobar que CORS está configurado correctamente, puedes usar curl para simular una petición preflight:
curl -v \
-X OPTIONS \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
http://localhost:8000/api/endpoint
La respuesta debe incluir las cabeceras:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, ...
Access-Control-Allow-Headers: Content-Type, Authorization
Si estas cabeceras aparecen en la respuesta, CORS está configurado correctamente y el navegador permitirá la petición.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en FastAPI
Documentación oficial de FastAPI
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, FastAPI es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de FastAPI
Explora más contenido relacionado con FastAPI y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender qué es CORS y por qué los navegadores bloquean peticiones entre dominios. Configurar CORSMiddleware en FastAPI para permitir orígenes específicos. Usar comodines y configuraciones dinámicas de CORS para distintos entornos. Habilitar credenciales CORS para peticiones con cookies y cabeceras de autorización. Configurar CORS correctamente para entornos de desarrollo y producción.