La correction CORS se fait sur l’API ou son proxy. Une origine comprend protocole, hôte et port : localhost:5173 et localhost:3000 sont deux origines distinctes.
Identifier la requête bloquée
Dans les outils de développement du navigateur, ouvrez Réseau, reproduisez l’appel et examinez l’URL, le statut et les en-têtes de réponse.
| Observation | Point à vérifier |
|---|---|
Une requête OPTIONS échoue |
Routage du preflight, authentification du proxy, méthodes et en-têtes autorisés |
OPTIONS réussit, mais l’appel échoue |
En-têtes CORS sur la réponse réelle, y compris les erreurs |
| L’origine autorisée ne correspond pas | Protocole, port, localhost contre 127.0.0.1 |
| Aucun statut HTTP exploitable | DNS, certificat TLS, serveur arrêté ou contenu HTTP appelé depuis HTTPS |
Deux en-têtes Access-Control-Allow-Origin |
CORS configuré à la fois dans le proxy et l’application |
Un échec de connexion ou une réponse 500 peut se présenter comme un problème CORS si le navigateur ne peut pas lire la réponse. Consultez aussi les logs de l’API.
Contrôler le preflight
Pour une API locale sur le port 3000, cet exemple simule la vérification précédant un POST JSON avec un en-tête d’authentification :
curl -i -X OPTIONS http://localhost:3000/api/items -H "Origin: http://localhost:5173" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: content-type,authorization"
Sous Windows PowerShell, remplacez curl par curl.exe pour appeler le binaire plutôt qu’un éventuel alias. Adaptez l’URL, l’origine, la méthode et les en-têtes à la requête réellement observée.
Une réponse adaptée à ce cas contient par exemple :
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Authorization
Vary: Origin
Le preflight n’envoie pas le jeton Bearer ni les cookies de la requête réelle. Le proxy et l’API doivent donc pouvoir répondre à OPTIONS sans exiger ces identifiants. L’authentification reste obligatoire sur la requête métier.
Configurer l’API au bon endroit
Déclarez une liste d’origines autorisées dans le middleware CORS du backend. Une origine ne contient ni chemin ni slash final. Si plusieurs frontends sont acceptés, renvoyez l’origine reçue seulement après l’avoir validée dans cette liste, avec Vary: Origin ; ne renvoyez pas une liste séparée par des virgules.
Placez la gestion CORS avant les routes et les réponses d’erreur. La réponse réelle doit elle aussi contenir Access-Control-Allow-Origin, même quand elle renvoie 401, 403 ou 500. Les autorisations de méthode et d’en-têtes concernent surtout la réponse au preflight.
Pour rendre lisible un en-tête personnalisé comme X-Request-Id depuis JavaScript, ajoutez Access-Control-Expose-Headers: X-Request-Id sur la réponse réelle.
Si l’authentification utilise des cookies
Pour envoyer les cookies avec un appel entre origines différentes :
const response = await fetch("http://localhost:3000/api/session", {
credentials: "include",
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
Le serveur doit renvoyer Access-Control-Allow-Credentials: true et une origine explicite, sur le preflight lorsqu’il existe et sur la réponse réelle. Access-Control-Allow-Origin: * n’est pas compatible avec ce mode.
Vérifiez aussi le domaine, le chemin et les attributs du cookie. Pour un usage réellement cross-site, SameSite=None; Secure est généralement nécessaire avec HTTPS ; les politiques de blocage des cookies tiers peuvent malgré tout s’appliquer. Deux origines différentes ne sont pas forcément deux sites différents.
CORS ne remplace ni l’authentification ni la protection CSRF des opérations utilisant des cookies.
Vérifier dans le navigateur
Rejouez l’appel depuis le frontend : curl affiche les en-têtes mais n’applique pas CORS. Vérifiez que le JavaScript peut lire la réponse attendue, puis testez également une erreur d’authentification. Si une ancienne réponse de preflight reste utilisée, essayez dans une nouvelle session de navigateur.
mode: "no-cors" donne une réponse opaque, illisible par votre code. Désactiver la sécurité du navigateur masque le problème. Un proxy de développement peut faciliter le travail local, mais la configuration de production doit être traitée séparément.

