Si vous avez vu une erreur CORS en rouge dans la console de votre navigateur, cela signifie que votre frontend envoie une requête à une API mais que le navigateur refuse de vous remettre la réponse. Le message classique est : Access to fetch at '...' from origin '...' has been blocked by CORS policy. À première vue on dirait que le serveur a planté, mais la requête atteint généralement le serveur et une réponse revient bel et bien — le navigateur refuse simplement de transmettre cette réponse à votre JavaScript à cause d'une règle de sécurité. Dans cet article, j'explique ce qu'est réellement cette erreur, les notions d'origine et de preflight, et comment résoudre durablement le problème avec les bons en-têtes de réponse.
Qu'est-ce qu'une erreur CORS exactement ?
CORS signifie Cross-Origin Resource Sharing (partage de ressources entre origines). Par défaut, les navigateurs appliquent la same-origin policy (politique de même origine) : le JavaScript d'une page ne peut lire que les réponses appartenant à la même origine. Une origine se compose de trois parties : le protocole, le domaine et le port. Ainsi https://site.com, http://site.com, https://api.site.com et https://site.com:8080 sont toutes des origines différentes.
Si votre frontend tourne sur http://localhost:3000 et votre API sur http://localhost:8000, ce sont deux origines distinctes, et le navigateur bloque la réponse tant que le serveur n'accorde pas explicitement la permission. CORS est la manière standardisée d'accorder cette permission : le serveur ajoute des en-têtes spéciaux à sa réponse pour dire « j'autorise cette origine à lire mes données ».
La distinction clé : la solution est sur le serveur, pas sur le client
C'est l'incompréhension la plus fréquente. CORS est un mécanisme du navigateur, mais la permission vient du serveur. Vous ne pouvez pas résoudre l'erreur en modifiant les options de fetch côté JavaScript ; la solution est que l'API envoie les bons en-têtes de réponse. L'en-tête clé est :
Access-Control-Allow-Origin: https://site.com
Si cet en-tête est absent de la réponse, ou s'il ne correspond pas à l'origine qui émet la requête, le navigateur la bloque. Quand vous envoyez la même requête depuis Postman ou curl, aucun problème — car ce ne sont pas des navigateurs et ils n'appliquent pas la same-origin policy. C'est précisément pour cela que l'erreur n'apparaît que dans le navigateur.
Qu'est-ce qu'une requête preflight (OPTIONS) ?
Avant certaines requêtes, le navigateur envoie une requête preflight avant la vraie. Il s'agit d'une « demande de permission » effectuée avec la méthode OPTIONS. Le navigateur demande : « Puis-je envoyer une requête depuis cette origine, avec cette méthode, avec ces en-têtes ? »
Un preflight est déclenché parce que la requête n'est pas « simple ». Une requête nécessite un preflight si elle utilise une méthode autre que GET, POST ou HEAD (par ex. PUT, DELETE, PATCH), ou si elle transporte des en-têtes personnalisés (par ex. Authorization, Content-Type: application/json). Le serveur doit renvoyer les méthodes et en-têtes autorisés dans sa réponse OPTIONS :
Access-Control-Allow-Origin: https://site.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Access-Control-Max-Age indique au navigateur combien de secondes mettre en cache la réponse preflight, ce qui évite un appel OPTIONS à chaque requête. Si le serveur renvoie un 404 ou un 405 à la requête OPTIONS, c'est la première chose à corriger.
Corriger l'erreur CORS côté serveur
La solution varie selon le framework, mais la logique est identique : ajouter les bons en-têtes. Dans une API Node.js basée sur Express, le paquet officiel cors est la voie la plus propre :
const cors = require('cors');
app.use(cors({
origin: 'https://site.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
Dans Laravel, la configuration CORS se trouve dans config/cors.php et le middleware HandleCors l'applique automatiquement. Vous y définissez les origines autorisées :
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://site.com'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
Sur un serveur web comme Nginx ou Apache, vous pouvez aussi ajouter les en-têtes directement, mais les gérer au niveau applicatif est généralement plus souple car vous pouvez valider l'origine de manière conditionnelle.
Travailler avec des identifiants (cookies et tokens)
Si vos requêtes transportent des cookies ou des données d'authentification, deux règles s'appliquent. Premièrement : vous devez activer credentials: 'include' côté client :
fetch('https://api.site.com/data', {
credentials: 'include',
});
Deuxièmement, et c'est le point le plus critique : lorsque des identifiants sont utilisés, l'en-tête Access-Control-Allow-Origin ne peut pas être * (joker). Le navigateur le refuse pour des raisons de sécurité ; vous devez indiquer l'origine exacte. Le serveur doit aussi envoyer Access-Control-Allow-Credentials: true. Ignorer ces deux règles est l'une des causes les plus fréquentes de l'erreur déroutante « impossible d'utiliser le joker avec des identifiants ».
Erreurs fréquentes et liste de contrôle rapide
- Conflit joker + identifiants : si vous envoyez des cookies, utilisez l'origine exacte au lieu de
*. - OPTIONS sans réponse : assurez-vous que votre serveur renvoie une réponse 2xx au preflight.
- Différence de slash final :
https://site.comethttps://site.com/peuvent casser la correspondance d'origine ; comparez précisément protocole + hôte + port. - Contournement par proxy : en développement, vous pouvez proxifier le serveur de dev frontend (par ex. Vite) vers l'API pour que les requêtes semblent venir de la même origine.
Questions fréquentes
Puis-je corriger l'erreur CORS en ne modifiant que le code frontend ?
Non. Les bons en-têtes de réponse doivent venir du serveur. Les seules choses possibles côté frontend sont l'utilisation d'un proxy en développement ou la correction de votre réglage credentials ; la permission réelle est toujours accordée côté API.
Pourquoi ça marche dans Postman mais j'obtiens une erreur CORS dans le navigateur ?
Parce que CORS est une politique de sécurité appliquée uniquement par le navigateur. Postman et curl n'appliquent pas la same-origin policy, donc la même requête fonctionne sans souci. Cela indique que le serveur tourne mais que les en-têtes CORS manquent.
Est-il sûr d'utiliser Access-Control-Allow-Origin: * ?
C'est acceptable pour une API publique sans identifiants. Mais pour des API qui fonctionnent avec des cookies/tokens ou servent des données sensibles, n'utilisez pas le joker ; listez explicitement les origines autorisées.
Vous voulez corriger durablement et en toute sécurité la configuration CORS de votre API ? Examinons ensemble les réglages d'origine, de preflight et d'identifiants entre votre frontend et votre backend, et construisons une solution adaptée à votre projet — contactez-moi.