Vous avez probablement vu cette erreur : "Access to fetch has been blocked by CORS policy". Votre code est correct, votre API fonctionne, mais le navigateur refuse obstinément de vous donner la réponse. Pourquoi ? C'est l'histoire des CORS, une sécurité fondamentale du web que tout développeur devrait comprendre.
Imaginez un immeuble de bureaux. Chaque entreprise occupe un étage. Vous travaillez au 3e étage (votre site web). Vous avez besoin d'un document du 7e étage (une API externe).
Vous montez au 7e, prenez le document, redescendez. Aucune vérification.
Problème : n'importe qui pourrait voler des documents dans tous les bureaux.
Le 7e étage a un vigile. Il vérifie votre badge. Si le 7e a autorisé le 3e étage, vous passez. Sinon, accès refusé.
Sécurité : seuls les étages autorisés peuvent accéder aux documents.
Dans le web : les "étages" sont des origines (domaine + port + protocole). Le "vigile" est votre navigateur. Le "badge" est le header CORS envoyé par le serveur distant.
Une origine, c'est la combinaison de 3 éléments :
Protocole + Domaine + Port
https:// monsite.fr :443
Exemples d'origines DIFFERENTES :
https://monsite.fr vs https://api.monsite.fr (domaine !)
https://monsite.fr vs http://monsite.fr (protocole !)
http://localhost:3000 vs http://localhost:8000 (port !)
Exemples de MEME origine :
https://monsite.fr/page1 et https://monsite.fr/page2 (seul le chemin change)Dès qu'un seul de ces 3 éléments diffère, le navigateur considère que ce sont deux origines différentes. Et c'est là que les CORS entrent en jeu.
Votre site https://monsite.fr veut afficher des données depuis https://api.weather.com. Votre code JavaScript :
// Votre code sur monsite.fr
const response = await fetch('https://api.weather.com/paris');
const data = await response.json();
console.log(data.temperature); // ← Vous n'arriverez jamais iciErreur dans la console :
Access to fetch at 'https://api.weather.com/paris' from origin 'https://monsite.fr' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Ce qui est frustrant : la requête a bien été envoyée et le serveur a bien répondu. Mais le navigateur bloque la réponse et refuse de vous la donner.
*) → la réponse est transmise au JavaScriptLe point crucial : c'est le navigateur qui bloque, pas le serveur. Le serveur a bien traité la requête. Mais le navigateur refuse de transmettre la réponse au JavaScript si les headers CORS ne sont pas présents.
Pour certaines requêtes plus complexes (POST avec JSON, PUT, DELETE, headers personnalisés), le navigateur envoie d'abord une requête préliminaire appelée "preflight" :
OPTIONS au serveur distant : "Est-ce que monsite.fr a le droit de faire un POST avec du JSON ?"// Ce que le navigateur envoie EN COULISSES avant votre requête :
OPTIONS /api/data HTTP/1.1
Host: api.weather.com
Origin: https://monsite.fr
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
// Ce que le serveur doit répondre pour autoriser :
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://monsite.fr
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 3600Sans CORS, n'importe quel site web pourrait faire des requêtes vers n'importe quel autre site en utilisant vos cookies et sessions. C'est catastrophique pour la sécurité.
fetch('https://mabanque.fr/virement', { method: 'POST', body: 'montant=10000&vers=pirate' })Avec les CORS : Le navigateur bloque automatiquement cette requête car mabanque.fr n'a jamais autorisé jeu-gratuit.com dans ses headers CORS. Votre argent est en sécurité.
Attention : tout n'est pas bloqué par les CORS. Seules les requêtes JavaScript le sont. Voici un tableau récapitulatif :
| Type de requête | Exemple | Soumis aux CORS ? |
|---|---|---|
| fetch() / XMLHttpRequest | fetch('https://api.com/data') |
Oui, bloqué |
| Lien <a href> | Cliquer sur un lien externe | Non, autorisé |
| Image <img src> | Afficher une image externe | Non, autorisé |
| Script <script src> | Charger une lib externe (jQuery, etc.) | Non, autorisé |
| Formulaire <form> | Soumettre un formulaire classique | Non, autorisé |
| CSS <link> | Charger un fichier CSS externe | Non, autorisé |
| Video/Audio | Lire un média externe | Non, autorisé |
| Serveur (Python, Node...) | requests.get('https://api.com') |
Non, pas concerné |
Point clé : Les CORS sont une sécurité du navigateur, pas du serveur. Un serveur Python, Node.js ou Java peut appeler n'importe quelle API sans aucune restriction CORS. Seul le JavaScript exécuté dans un navigateur est concerné.
La solution idéale : le serveur distant ajoute les bons headers CORS. Si vous contrôlez l'API, c'est simple :
# Python (FastAPI)
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://monsite.fr"], # Autoriser votre site
allow_methods=["GET", "POST"],
allow_headers=["*"],
)
# Node.js (Express)
const cors = require('cors');
app.use(cors({ origin: 'https://monsite.fr' }));Attention : allow_origins=["*"] autorise TOUS les sites. C'est pratique pour le développement, mais dangereux en production si votre API contient des données sensibles.
Si vous ne contrôlez pas l'API distante (API publique, service tiers), la solution est de créer un proxy sur votre propre serveur :
# Proxy en Python (FastAPI)
@app.get("/api/meteo")
async def proxy_meteo(ville: str):
# Votre serveur appelle l'API (pas de CORS !)
response = requests.get(f"https://api.weather.com/{ville}")
return response.json()
# Le navigateur appelle VOTRE serveur (meme origine = pas de CORS)
# fetch('/api/meteo?ville=paris') ← OK !Pourquoi ça marche ?
1. Le navigateur appelle votre serveur (meme domaine) → pas de CORS
2. Votre serveur appelle l'API externe → les serveurs ne sont jamais soumis aux CORS
3. Votre serveur renvoie la réponse au navigateur → tout le monde est content
Pendant le développement, quelques astuces (ne JAMAIS utiliser en production) :
Des extensions comme "CORS Unblock" désactivent les CORS dans votre navigateur. Pratique pour tester, mais ça supprime une protection de sécurité.
Les outils comme Vite, Webpack ou Create React App intègrent un proxy de développement qui contourne les CORS automatiquement.
// vite.config.js
server: {
proxy: {
'/api': 'http://localhost:8000'
}
}| Header | Envoyé par | Rôle |
|---|---|---|
Origin |
Navigateur | Indique d'où vient la requête |
Access-Control-Allow-Origin |
Serveur | Liste les origines autorisées (* = toutes) |
Access-Control-Allow-Methods |
Serveur | Methodes HTTP autorisées (GET, POST, PUT...) |
Access-Control-Allow-Headers |
Serveur | Headers personnalisés autorisés |
Access-Control-Allow-Credentials |
Serveur | Autorise l'envoi de cookies cross-origin |
Access-Control-Max-Age |
Serveur | Durée de cache du preflight (en secondes) |
Cause : Le serveur ne renvoie pas le header CORS
Solution : Ajouter le middleware CORS sur le serveur ou utiliser un proxy
Cause : Le serveur ne gère pas les requêtes OPTIONS
Solution : Configurer le serveur pour répondre aux requêtes OPTIONS avec les bons headers
Cause : Vous envoyez des cookies mais allow_credentials n'est pas activé
Solution : Ajouter Access-Control-Allow-Credentials: true et ne pas utiliser * comme origine
Cause : Votre methode HTTP (POST, PUT, DELETE) n'est pas dans Allow-Methods
Solution : Ajouter la methode dans la liste des methodes autorisées
Quand vous avez une erreur CORS, suivez ces étapes :
Access-Control-Allow-Origin ?curl -I https://api.com/endpoint pour voir les headers sans restriction CORS# Tester les headers CORS avec curl :
curl -I -H "Origin: https://monsite.fr" https://api.example.com/data
# Cherchez dans la réponse :
# Access-Control-Allow-Origin: https://monsite.fr ← OK
# Access-Control-Allow-Origin: * ← OK (mais moins securise)
# (absent) ← Probleme !A retenir
Les CORS ne sont pas un bug. C'est une protection essentielle qui empêche les sites malveillants d'agir en votre nom. Quand vous rencontrez une erreur CORS, ne cherchez pas à "désactiver" la sécurité : trouvez la bonne solution (configurer les headers ou utiliser un proxy serveur).
Et rappelez-vous : seuls les navigateurs appliquent les CORS. Si votre serveur backend a besoin d'appeler une API externe, il n'y aura jamais de problème CORS.
Cet article vous a aidé à comprendre les CORS ? Vous avez d'autres questions sur la sécurité web ?
💼 LinkedIn 🐙 GitHub 📧 EmailArticle rédigé par François MENGUY · Février 2026