JavaScript Fetch : requêtes cross-origin (CORS)
Apprenez à effectuer des requêtes cross-origin avec l'API Fetch JavaScript : fonctionnement de CORS, options mode et credentials, requêtes preflight et bonnes pratiques de gestion des erreurs.
Les requêtes cross-origin permettent à une page web de charger des données depuis un serveur sur une origine différente de celle de la page elle-même. Elles alimentent presque toutes les applications modernes : appel d'une API publique, communication avec votre propre backend sur un autre sous-domaine, ou intégration d'un service tiers. Cette page explique comment fonctionnent les règles CORS du navigateur, comment effectuer des appels cross-origin avec l'API Fetch, et comment gérer correctement les credentials, les requêtes preflight et les erreurs.
Ce qui constitue une origine différente
Une origine est la combinaison protocole + hôte + port. Deux URL partagent la même origine uniquement lorsque ces trois éléments correspondent. Si l'un d'eux diffère, une requête entre elles est cross-origin.
Requête de https://app.example.com vers… | Même origine ? | Pourquoi |
|---|---|---|
https://app.example.com/api/users | Oui | protocole, hôte et port identiques |
http://app.example.com/api | Non | protocole différent (http vs https) |
https://api.example.com/users | Non | hôte différent (les sous-domaines comptent) |
https://app.example.com:8443/api | Non | port différent |
Les requêtes same-origin sont sans restriction. Les requêtes cross-origin sont régies par CORS.
Ce qu'est CORS (et ce qu'il n'est pas)
Cross-Origin Resource Sharing (CORS) est un mécanisme de sécurité du navigateur qui détermine si JavaScript sur une origine est autorisé à lire une réponse provenant d'une autre origine. La décision est prise par le serveur, qui l'accepte en envoyant des en-têtes de réponse HTTP spécifiques. Le navigateur les applique ensuite.
Deux choses prêtent facilement à confusion :
- CORS n'est pas quelque chose que vous corrigez uniquement dans le code front-end. Si le serveur n'envoie pas les bons en-têtes, aucune option de
fetchne permettra de lire la réponse. - La requête atteint souvent encore le serveur et s'y exécute ; CORS bloque uniquement la lecture de la réponse par votre script. C'est pourquoi CORS ne remplace pas l'authentification.
Effectuer une requête cross-origin avec Fetch
L'API Fetch est la méthode moderne basée sur les promesses pour effectuer des requêtes HTTP. Un simple appel fetch vers une autre origine est déjà cross-origin — le navigateur gère CORS automatiquement.
Cela fonctionne parce que jsonplaceholder.typicode.com renvoie Access-Control-Allow-Origin: *. Sans cela, le navigateur bloquerait la lecture et fetch serait rejeté. Consultez le chapitre API Fetch pour le modèle complet requête/réponse.
Les en-têtes serveur qui rendent cela possible
Lorsque vous effectuez une requête cross-origin, le serveur doit inclure des en-têtes CORS pour l'autoriser. Ceux que vous rencontrerez le plus souvent :
Access-Control-Allow-Origin— quelle(s) origine(s) peuvent lire la réponse (une origine exacte, ou*pour toutes).Access-Control-Allow-Methods— quelles méthodes HTTP sont autorisées (utilisé dans les réponses preflight).Access-Control-Allow-Headers— quels en-têtes de requête le client peut envoyer (utilisé dans les réponses preflight).Access-Control-Allow-Credentials— si les cookies/auth peuvent être envoyés (doit êtretruepour autoriser les credentials).
Vous configurez ces éléments sur le serveur, pas dans fetch. Le front-end ne contrôle que la requête.
L'option mode
L'option mode de Fetch déclare comment le traitement cross-origin doit se comporter :
'cors'— valeur par défaut. La requête n'est autorisée que si le serveur renvoie des en-têtes CORS correspondants ; sinon la lecture est bloquée.'same-origin'— échoue immédiatement pour toute URL cross-origin.'no-cors'— envoie la requête mais renvoie une réponse opaque : vous ne pouvez pas lire son statut, ses en-têtes ou son corps. Utile uniquement pour les cas fire-and-forget comme la mise en cache d'une image dans un service worker.
Comme 'cors' est déjà la valeur par défaut, vous n'avez rarement besoin de le définir, mais être explicite documente l'intention :
Sur le serveur, évitez Access-Control-Allow-Origin: * en production. N'autorisez que les origines spécifiques en lesquelles vous avez confiance.
Envoi de credentials
Par défaut, les requêtes fetch cross-origin n'envoient pas de cookies ni d'en-têtes HTTP-auth. Pour les inclure, définissez l'option credentials sur 'include'. Le serveur doit également répondre avec Access-Control-Allow-Credentials: true et indiquer votre origine exacte dans Access-Control-Allow-Origin — * est rejeté lorsque des credentials sont impliqués. Consultez Cookies et document.cookie pour comprendre comment les cookies se comportent entre les sites.
async function fetchWithCredentials(url) {
try {
const response = await fetch(url, {
mode: 'cors',
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchWithCredentials('https://api.crossorigin.com/secure-data');Lorsqu'une politique CORS bloque une lecture cross-origin, le navigateur ne transmet pas la réponse à votre script. À la place, fetch est rejeté avec une TypeError ("Failed to fetch"), donc l'erreur atterrit dans votre bloc catch — et non dans la vérification if (!response.ok).
Requêtes preflight
Pour les requêtes que le navigateur considère comme potentiellement dangereuses, il envoie d'abord automatiquement une requête OPTIONS — un preflight — pour demander au serveur si la vraie requête est autorisée. Votre code n'émet jamais cet appel OPTIONS ; c'est le navigateur qui le fait pour vous.
Une requête déclenche un preflight lorsqu'elle n'est pas une "requête simple", c'est-à-dire lorsqu'elle :
- utilise une méthode autre que
GET,HEADouPOST; - inclut des en-têtes de requête personnalisés (par exemple
AuthorizationouX-Api-Key) ; - utilise un
Content-Typeautre queapplication/x-www-form-urlencoded,multipart/form-dataoutext/plain(l'envoi de JSON avecapplication/jsonest le déclencheur le plus courant).
Un POST JSON typique coûte donc deux allers-retours — le preflight, puis la vraie requête :
async function createPost(url, payload) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }, // triggers a preflight
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
}
}
createPost('https://api.example.com/posts', { title: 'Hello' });Pour que le preflight réussisse, le serveur doit répondre à la requête OPTIONS avec Access-Control-Allow-Methods et Access-Control-Allow-Headers couvrant ce que la vraie requête utilisera. En cas d'échec, le navigateur génère une erreur réseau et votre vraie requête n'est jamais envoyée.
Distinguer les modes d'échec
Les erreurs CORS sont notoirement déroutantes parce que le navigateur masque les détails pour des raisons de sécurité. Utilisez cette liste de vérification :
fetchest rejeté ("Failed to fetch") — généralement un blocage CORS, une défaillance réseau, ou un preflight échoué. Vérifiez la console du navigateur pour le message CORS spécifique ; il n'est pas exposé à JavaScript.response.okestfalse— la requête a réussi et CORS est passé, mais le serveur a renvoyé un statut 4xx/5xx. Il s'agit d'une erreur applicative, pas d'une erreur CORS.- Cookies non envoyés — vous avez oublié
credentials: 'include', ou le serveur ne renvoie pasAccess-Control-Allow-Credentials: trueavec une origine spécifique.
Encapsulez les appels dans try/catch et vérifiez response.ok séparément, comme le font les exemples ci-dessus.
Bonnes pratiques
- Restreignez
Access-Control-Allow-Origin. Listez les origines exactes en lesquelles vous avez confiance plutôt que*, surtout lorsque des credentials sont impliqués (*est rejeté avec des credentials). - Utilisez toujours HTTPS. Cela protège les données en transit et est requis pour de nombreuses API de contexte sécurisé.
- Réduisez les preflights. Évitez les en-têtes personnalisés inutiles, et laissez le serveur envoyer
Access-Control-Max-Agepour que le navigateur mette en cache les résultats preflight. - Gérez les erreurs avec élégance. Séparez les échecs de transport/CORS (
catch) des erreurs de statut HTTP (!response.ok) et affichez des messages utiles à l'utilisateur. - Ne comptez pas sur CORS pour la sécurité. Il contrôle ce que les navigateurs laissent les scripts lire ; il n'authentifie pas l'appelant. Validez et autorisez chaque requête côté serveur.
Chapitres associés
- API Fetch — la base de chaque requête présentée dans cette page.
- Annuler un fetch — annuler des requêtes lentes ou indésirables.
- Travailler avec JSON — analyser et stringifier les corps de requêtes et de réponses.
- Cookies : document.cookie — comment les cookies interagissent avec les credentials cross-origin.
Résumé
Une requête est cross-origin lorsque son protocole, son hôte ou son port diffère de ceux de la page. CORS laisse le serveur décider quelles origines peuvent lire la réponse, et le navigateur applique cette décision. Avec Fetch, le mode 'cors' est la valeur par défaut ; utilisez l'option credentials pour envoyer des cookies, attendez-vous à une requête preflight OPTIONS pour les appels non simples, et gérez les échecs CORS dans catch tout en vérifiant response.ok pour les erreurs de niveau HTTP.