backend / fastapi
CORS
Explication
Ce que vous allez apprendre
- Comprendre pourquoi le navigateur bloque par défaut une requête cross-origin
- Configurer
CORSMiddlewareavec les bonnes origines autorisées - Comprendre le rôle de la requête preflight
OPTIONSdéclenchée automatiquement - Éviter la combinaison interdite
allow_origins=["*"]avecallow_credentials=True - Autoriser dynamiquement un ensemble de sous-domaines avec
allow_origin_regex
Dans quel contexte ?
Le frontend React hébergé sur https://monapp.com tente d'appeler l'API FastAPI hébergée sur https://api.monapp.com, mais la console du navigateur affiche "has been blocked by CORS policy". Aucune erreur n'apparaît côté serveur : la requête n'a même pas atteint l'API, car le navigateur bloque la réponse avant de la transmettre au code JavaScript, faute d'en-tête Access-Control-Allow-Origin correspondant. Ajouter CORSMiddleware avec allow_origins=["https://monapp.com"] sur l'API résout le problème.
Le problème, d'abord
Par défaut, les navigateurs appliquent une règle de sécurité stricte appelée "same-origin policy". Une page web chargée depuis un domaine ne peut PAS, par défaut, faire une requête vers une API hébergée sur un domaine différent.
Même si cette API appartient à la même équipe. C'est une protection contre des sites malveillants qui tenteraient d'utiliser silencieusement les identifiants d'un utilisateur sur un autre site.
CORS permet de lever cette restriction, mais de façon contrôlée. C'est un mécanisme où le SERVEUR indique explicitement, via des en-têtes HTTP, quelles origines il autorise.
Le navigateur, avant certaines requêtes "sensibles", envoie automatiquement une requête préliminaire appelée "preflight". Il bloque ou laisse passer la vraie requête selon la réponse reçue à cette préliminaire.
Une idée reçue fréquente chez les débutants mérite d'être corrigée ici. On pourrait penser qu'on peut contourner une erreur CORS depuis le code JavaScript du frontend.
C'est faux : la vérification est faite par le NAVIGATEUR, en se basant sur ce que répond le SERVEUR. La seule vraie solution est donc de configurer correctement le serveur, via CORSMiddleware, pour qu'il autorise explicitement les origines légitimes.
| Configuration | allow_credentials=True compatible ? |
|---|---|
allow_origins=["https://monapp.com"] | Oui |
allow_origins=["*"] | Non — interdit par la spécification CORS |
allow_origin_regex=r"https://.*\.monapp.com" | Oui |
Piège de sécurité
Autoriser allow_origins=["*"] en même temps que allow_credentials=True est en réalité interdit par la spécification CORS elle-même, car cette combinaison ouvrirait la porte à n'importe quel site tiers pour utiliser les identifiants d'un utilisateur connecté. Le wildcard "*" ne devrait être utilisé QUE pour une API réellement publique et sans notion de session ou de cookies.
Commandes & code
CORS
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["https://monapp.com", "https://admin.monapp.com"],
allow_credentials=True, # autorise l'envoi de cookies cross-origin
allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
allow_headers=["Authorization", "Content-Type"],
expose_headers=["X-Request-ID"], # headers custom lisibles par le JS du frontend
max_age=3600, # durée de cache du préflight OPTIONS
)# Origines dynamiques selon l'environnement — configuration centralisée
from app.core.config import settings
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins, # ex. issu de la variable d'env CORS_ORIGINS
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)# app/core/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
cors_origins: list[str] = ["http://localhost:3000"] # dev par défaut
class Config:
env_file = ".env"
settings = Settings()# .env.production
CORS_ORIGINS=["https://monapp.com","https://admin.monapp.com"]# Piège fréquent : allow_origins=["*"] est INCOMPATIBLE avec allow_credentials=True
# (la spec CORS l'interdit explicitement pour des raisons de sécurité)
# MAUVAIS — ne fonctionnera pas avec des cookies/credentials
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True, # le navigateur rejettera cette combinaison
)
# BON — wildcard uniquement pour une API publique SANS credentials
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=False,
)# Origin regex — pour autoriser dynamiquement des sous-domaines (previews Vercel par ex.)
app.add_middleware(
CORSMiddleware,
allow_origin_regex=r"https://.*\.monapp-preview\.com",
allow_credentials=True,
)Résumé
allow_originsdoit lister explicitement les domaines autorisés, jamais"*"en production avec cookies.allow_credentials=Trueetallow_origins=["*"]sont mutuellement incompatibles selon la spécification CORS.allow_origin_regexest utile pour autoriser un ensemble dynamique de sous-domaines (previews, environnements éphémères).- Le middleware CORS gère automatiquement les requêtes preflight
OPTIONS, sans code supplémentaire.
Exercices pratiques
Mission : le frontend bloqué par CORS et la config dangereuse
Objectif : Diagnostiquer une erreur CORS entre deux domaines distincts et corriger une configuration CORS incompatible avec les credentials.
Contexte
Le frontend React hébergé sur https://monapp.com appelle l'API FastAPI sur https://api.monapp.com et la console affiche "has been blocked by CORS policy". Un développeur, pour aller vite, a alors configuré CORSMiddleware avec allow_origins=["*"] et allow_credentials=True, mais les requêtes avec cookies échouent toujours.