CORS expliqué : preflight, en-têtes et correction des erreurs CORS
CORS expliqué
CORS expliqué : preflight, en-têtes, erreurs et correctifs | We.Inc
Comment fonctionne CORS : requêtes simples ou preflight, chaque en-tête Access-Control, règles des credentials, les erreurs de console courantes et un correctif serveur pour Express, Next.js et nginx.
CORS (cross-origin resource sharing) permet à un serveur d'indiquer au navigateur que du JavaScript provenant d'une autre origine peut lire ses réponses. Le serveur répond avec Access-Control-Allow-Origin et les en-têtes associés. Les requêtes qui utilisent d'autres méthodes, des en-têtes personnalisés ou un corps JSON envoient d'abord une requête preflight OPTIONS. Le correctif se fait toujours sur le serveur que vous appelez, jamais sur la page.
Les en-têtes de réponse CORS en un coup d'œil
En-tête
Ce qu'il autorise
Access-Control-Allow-Origin
Quelle origine peut lire la réponse (une seule origine ou *)
Access-Control-Allow-Methods
Méthodes autorisées après un preflight
Access-Control-Allow-Headers
En-têtes de requête autorisés après un preflight
Access-Control-Allow-Credentials
Cookies et authentification envoyés avec la requête (la valeur doit être true)
Access-Control-Expose-Headers
En-têtes de réponse que le script peut lire
Access-Control-Max-Age
Durée en secondes pendant laquelle une réponse preflight peut être mise en cache
Une erreur CORS donne l'impression que votre code est cassé, alors que la requête a généralement bien atteint le serveur. Le navigateur a simplement refusé de transmettre la réponse à votre script. Le simulateur ci-dessous vous permet de décrire une requête et une configuration serveur, puis vous indique si un preflight est déclenché, si le navigateur laisse passer la réponse et quel message vous verriez dans la console.
Définissez l'origine de la page, la méthode, un éventuel en-tête personnalisé et si des cookies sont envoyés.
Définissez la réponse du serveur : origine autorisée, méthodes, en-têtes et indicateur credentials.
Lisez le verdict : si un preflight est nécessaire, s'il réussit, et la raison affichée dans la console en cas d'échec.
Copiez l'extrait serveur correspondant ci-dessous et ajoutez-le à l'API, pas au front-end.
Renvoyez l'origine exacte de la requête depuis une liste d'autorisation dès que des cookies sont en jeu, car le joker est refusé avec les credentials.
Ajoutez Vary: Origin dès que l'origine autorisée change selon la requête, pour que les caches ne servent pas la réponse d'un site à un autre.
Répondez à OPTIONS avec un statut 2xx et sans redirection. Un preflight qui tombe sur une redirection vers la page de connexion échoue à chaque fois.
Désactiver CORS dans votre propre navigateur ne fait que masquer le problème sur votre machine. Les visiteurs voient toujours l'erreur.
Les appels de serveur à serveur ne passent jamais par CORS. Si vous ne contrôlez pas l'API, appelez-la plutôt depuis votre propre backend.
Questions fréquentes
Pourquoi Postman ou curl fonctionnent alors que le navigateur échoue ?
CORS n'est appliqué que par les navigateurs, pour empêcher les scripts d'un site de lire les données d'un autre site avec vos cookies. Des outils comme curl n'exécutent pas de code de page non fiable, ils l'ignorent donc. La réponse du serveur était identique, seul le navigateur a décidé de la retenir.
Qu'est-ce qui déclenche une requête preflight ?
Toute méthode autre que GET, HEAD ou POST, tout en-tête hors de la courte liste autorisée (Accept, Accept-Language, Content-Language, Content-Type, Range), ou un Content-Type autre que l'encodage de formulaire, multipart ou text/plain. L'envoi de application/json est le déclencheur le plus courant.
Puis-je corriger CORS depuis le frontend ?
Non. Définir des en-têtes Access-Control sur votre requête ne sert à rien, et mode: no-cors vous donne une réponse opaque que votre script ne peut pas lire. Soit l'API ajoute les en-têtes, soit votre propre serveur récupère les données et les transmet.
Puis-je utiliser Access-Control-Allow-Origin: * avec des cookies ?
Non. Quand une requête transporte des credentials, le navigateur rejette une origine joker et exige l'origine exacte accompagnée de Access-Control-Allow-Credentials: true. Il en va de même pour les jokers dans les listes de méthodes, d'en-têtes et d'en-têtes exposés.
Pourquoi mon script ne peut-il pas lire un en-tête de réponse personnalisé ?
Par défaut, un script cross-origin ne voit qu'une courte liste d'en-têtes de réponse sûrs. Listez les autres dans Access-Control-Expose-Headers, par exemple X-Request-Id, et ils deviennent lisibles.
Même origine, et pourquoi le navigateur bloque la lecture
Une origine, c'est le schéma, l'hôte et le port ensemble. https://app.example.com et https://api.example.com sont des origines différentes, tout comme les versions http et https d'un même domaine. Par défaut, un script peut envoyer une requête vers une autre origine, mais ne peut pas lire ce qui revient.
CORS est le mécanisme d'autorisation explicite. L'API liste les origines auxquelles elle fait confiance et le navigateur applique cette liste pour le compte du visiteur. C'est pourquoi l'en-tête doit venir du serveur que vous appelez.
Requêtes simples et preflights
Un simple GET, ou un POST depuis un formulaire ordinaire, part directement avec un en-tête Origin. Le navigateur vérifie ensuite que la réponse contient une origine autorisée avant de la transmettre à votre code.
Tout ce qui va au-delà envoie d'abord une requête OPTIONS contenant Access-Control-Request-Method et Access-Control-Request-Headers. La vraie requête ne part que si le serveur approuve les deux. Access-Control-Max-Age permet au navigateur de mémoriser l'approbation, même si les navigateurs plafonnent cette durée.
Lire l'erreur dans la console
Pour des raisons de sécurité, votre JavaScript apprend seulement que le fetch a échoué, avec un TypeError et aucun détail. La vraie raison apparaît dans la console du navigateur, et elle nomme l'en-tête manquant ou incorrect. Comparez-la au tableau des messages ci-dessous.
Erreurs CORS courantes et comment les corriger
La formulation des messages varie selon le navigateur. Voici les raisons telles que Firefox les nomme sur MDN, Chrome dit la même chose avec des phrases plus longues.
Raisons CORS affichées dans la console et correctif côté serveur
Raison dans la console
Ce que cela signifie
Correctif sur le serveur
CORS header 'Access-Control-Allow-Origin' missing
La réponse ne contenait aucun en-tête allow-origin
Ajoutez l'en-tête à chaque réponse, y compris les erreurs
CORS header 'Access-Control-Allow-Origin' does not match
L'en-tête indique une autre origine
Renvoyez l'origine de l'appelant depuis votre liste d'autorisation
Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*'
Des cookies ont été envoyés mais le serveur a répondu avec *
Renvoyez l'origine exacte et Allow-Credentials: true
expected 'true' in CORS header 'Access-Control-Allow-Credentials'
Des credentials ont été envoyés mais l'indicateur est absent
Envoyez Access-Control-Allow-Credentials: true
Did not find method in CORS header 'Access-Control-Allow-Methods'
Le preflight n'a pas approuvé la méthode
Listez PUT, PATCH ou DELETE dans Allow-Methods
missing token in CORS header 'Access-Control-Allow-Headers' from CORS preflight channel
Un en-tête de requête comme Authorization n'a pas été approuvé
Listez-le dans Allow-Headers
CORS preflight channel did not succeed
OPTIONS a échoué, a été redirigé ou a renvoyé une erreur
Répondez à OPTIONS avec 204 et les en-têtes
Multiple CORS header 'Access-Control-Allow-Origin' not allowed
Deux couches (application et proxy) ont toutes deux ajouté l'en-tête
Définissez-le à un seul endroit
Extraits serveur
Express : liste d'autorisation avec credentials
Renvoie l'origine uniquement si elle figure dans la liste, répond aux preflights avec 204 et ajoute Vary pour que les caches séparent les origines.
Route handler Next.js
Un endpoint public sans cookies peut utiliser le joker. Exportez OPTIONS pour que le preflight obtienne sa propre réponse.
nginx devant une API
Le drapeau always conserve l'en-tête sur les réponses d'erreur aussi, pour qu'une erreur 500 affiche son vrai statut au lieu d'une erreur CORS.
Côté navigateur : envoyer des cookies en cross-origin
credentials: "include" est ce qui active les règles plus strictes des credentials sur le serveur.
Simulateur de requête CORS
Décrivez une requête et les en-têtes CORS du serveur pour voir si un preflight est envoyé et si le navigateur laisse votre script lire la réponse.
La requête
La réponse du serveur
Origine de la page
Origine de l'API
Méthode
Content-Type
Pas de corps
En-tête de requête supplémentaire (vide si aucun)
Envoyer les cookies (credentials: include)
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Credentials: true
Verdict
Même origine : CORS ne s'applique pas, la réponse est lisible.
Pas de preflight : il s'agit d'une requête simple.
Preflight nécessaire : le navigateur envoie d'abord OPTIONS.
Raison : la méthode n'est pas GET, HEAD ou POST.
Raison : l'en-tête {v} ne fait pas partie de la liste autorisée.
Raison : le Content-Type {v} ne fait pas partie des trois types simples.