Le premier appel vers une API hébergée ailleurs se termine presque toujours de la même façon : la console affiche un message de blocage, l'onglet réseau montre pourtant une réponse arrivée, et le code n'y a pas accès.
Ce mécanisme n'est ni un bug ni une sévérité inutile. Il empêche une page quelconque de lire, avec vos cookies, les données d'un site où vous êtes connecté.
Définition
Le CORS, pour partage des ressources entre origines, est le protocole par lequel un serveur autorise une page d'une autre origine à lire ses réponses. L'autorisation passe par des en-têtes HTTP, et c'est le navigateur qui la vérifie.
Rien ne s'écrit côté page pour la débloquer : un appel fetch() refusé se règle sur le serveur appelé, ou pas du tout.
Ce qu'est une origine
Une origine est le trio protocole, domaine et port. Une seule différence suffit à changer d'origine, ce que le tableau montre sur trois colonnes pour une page servie depuis https://app.exemple.com.
| URL appelée | Même origine | Ce qui diffère |
|---|---|---|
https://app.exemple.com/api | Oui | Rien, seul le chemin change |
https://api.exemple.com | Non | Le sous-domaine |
http://app.exemple.com | Non | Le protocole |
https://app.exemple.com:8443 | Non | Le port |
La deuxième ligne surprend souvent : un sous-domaine du même site reste une autre origine, et demande donc une autorisation explicite.
La requête préparatoire
Certaines requêtes partent directement. D'autres sont précédées d'un appel OPTIONS automatique, qui demande la permission avant d'envoyer quoi que ce soit. Ce détour se déclenche dès qu'une requête sort du cadre le plus simple : une méthode autre que GET ou POST, un en-tête sur mesure, ou un corps annoncé en application/json.
// Côté serveur, la réponse doit porter ces en-têtes
function autoriser(reponse, origine) {
reponse.setHeader("Access-Control-Allow-Origin", origine);
reponse.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE");
reponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
reponse.setHeader("Access-Control-Max-Age", "86400");
}Le dernier en-tête met la permission en cache, ce qui évite un aller-retour supplémentaire avant chaque appel. Chaque navigateur applique son propre plafond, souvent bien plus bas que la valeur demandée.
Le blocage protège la lecture de la réponse, pas le serveur. Une requête simple atteint bel et bien la destination avant d'être bloquée à la lecture : une écriture doit donc rester protégée côté serveur, indépendamment de ce mécanisme.
Questions fréquentes
Pourquoi le même appel fonctionne-t-il depuis un client REST ?
Parce que la règle est appliquée par le navigateur, et par lui seul. Ni un client comme Postman, ni une commande curl, ni un script Node.js ne tiennent compte de ces en-têtes. Un appel qui répond correctement en ligne de commande et échoue dans l'onglet est donc parfaitement normal.
Peut-on contourner le blocage depuis la page ?
Non, et c'est voulu. L'option mode: "no-cors" laisse partir la requête mais rend une réponse opaque, dont ni le corps ni le statut ne sont lisibles. La seule solution durable consiste à passer par votre propre serveur, qui appelle l'API et vous renvoie le résultat depuis votre origine.
Pourquoi l'étoile ne marche-t-elle pas avec les cookies ?
Parce que la valeur * est incompatible avec un appel qui transporte des identifiants. Il faut alors renvoyer l'origine exacte de l'appelant dans Access-Control-Allow-Origin, ajouter Access-Control-Allow-Credentials à vrai, et régler credentials sur include côté page. Cette configuration se met en place pas à pas dans la formation Node.js.