Les CORS expliqués simplement : pourquoi votre navigateur bloque vos requêtes

Les CORS expliqués simplement : pourquoi votre navigateur bloque vos requêtes

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.

L'analogie : l'immeuble et le badge

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).

🏢

Sans CORS (monde idéal)

Vous montez au 7e, prenez le document, redescendez. Aucune vérification.

Problème : n'importe qui pourrait voler des documents dans tous les bureaux.

🔐

Avec CORS (monde réel)

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.

C'est quoi une "origine" ?

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.

Le problème concret

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 ici

Erreur 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.

Comment ça fonctionne : le mécanisme en détail

Requête "simple" (GET classique)

Requête cross-origin : que se passe-t-il ?
Navigateur
monsite.fr
──fetch──>
Serveur distant
api.weather.com
1
Le navigateur envoie la requête avec un header Origin: https://monsite.fr
2
Le serveur distant reçoit la requête et renvoie sa réponse
3
Le navigateur inspecte les headers de la réponse
4a
Si le header Access-Control-Allow-Origin contient monsite.fr (ou *) → la réponse est transmise au JavaScript
4b
Si le header est absent ou ne correspond pas → la réponse est bloquée et le JavaScript reçoit une erreur

Le 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.

Requête "preflight" (OPTIONS)

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" :

Requête Preflight (pré-vol)
1
Preflight : Le navigateur envoie une requête OPTIONS au serveur distant : "Est-ce que monsite.fr a le droit de faire un POST avec du JSON ?"
2
Réponse du serveur : "Oui, j'autorise monsite.fr pour POST, avec les headers Content-Type, pendant 1 heure"
3
Requête réelle : Le navigateur envoie enfin la vraie requête POST
4
Réponse finale : Le serveur répond, le navigateur transmet au JavaScript
Le preflight est automatique et invisible. Le navigateur le fait de lui-même avant votre requête.
// 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: 3600

Pourquoi les CORS existent ?

Sans 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é.

Le scénario d'attaque (sans CORS)

Attaque CSRF : sans CORS, c'est possible
1
Vous êtes connecté sur mabanque.fr (vos cookies de session sont actifs)
2
Vous visitez un site malveillant : jeu-gratuit.com
3
Le JavaScript de jeu-gratuit.com fait en arrière-plan :
fetch('https://mabanque.fr/virement', { method: 'POST', body: 'montant=10000&vers=pirate' })
4
Le navigateur envoie la requête avec vos cookies de session bancaire !
5
Résultat : un virement de 10 000 euros vers le compte du pirate
Sans CORS, le site malveillant peut agir "en votre nom" sur n'importe quel site web

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é.

Ce qui est bloqué (et ce qui ne l'est pas)

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é.

Les solutions quand on est bloqué

Solution 1 : Le serveur autorise votre origine

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.

Solution 2 : Le proxy serveur

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 :

Sans proxy (bloqué)
Navigateur
monsite.fr
─ ✕ ─>
API externe
Pas de CORS
Le navigateur bloque la requête
Avec proxy (fonctionne)
Navigateur monsite.fr
↓ Meme origine
Votre serveur monsite.fr/api
↓ Pas de CORS
API externe Aucune restriction
Le serveur n'est pas soumis aux CORS
# 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

Solution 3 : En développement uniquement

Pendant le développement, quelques astuces (ne JAMAIS utiliser en production) :

Extension navigateur

Des extensions comme "CORS Unblock" désactivent les CORS dans votre navigateur. Pratique pour tester, mais ça supprime une protection de sécurité.

Proxy de dev

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' } }

Les headers CORS en un coup d'oeil

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)

Erreurs courantes et comment les résoudre

No 'Access-Control-Allow-Origin'

Cause : Le serveur ne renvoie pas le header CORS

Solution : Ajouter le middleware CORS sur le serveur ou utiliser un proxy

Preflight request failed

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

Credentials not supported

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

Method not allowed

Cause : Votre methode HTTP (POST, PUT, DELETE) n'est pas dans Allow-Methods

Solution : Ajouter la methode dans la liste des methodes autorisées

Checklist : diagnostic rapide

Quand vous avez une erreur CORS, suivez ces étapes :

  1. Ouvrez la console du navigateur (F12 > Console) et lisez le message d'erreur en entier
  2. Ouvrez l'onglet Network (F12 > Network) et regardez la requête qui a échoué
  3. Vérifiez les headers de la réponse : y a-t-il un Access-Control-Allow-Origin ?
  4. Vérifiez si c'est un preflight : y a-t-il une requête OPTIONS juste avant ?
  5. Testez avec curl : 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 !

Resumé visuel

CORS : le resumé complet
1
Votre JavaScript fait une requête vers un autre domaine
2
Le navigateur ajoute automatiquement le header Origin
3
Le serveur distant reçoit la requête et répond normalement
4
Le navigateur inspecte les headers CORS dans la réponse
5a
Header present et correct → la réponse est transmise au JavaScript
5b
Header absent ou incorrect → la réponse est bloquée, erreur dans la console

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.

Une question ? Un retour ?

Cet article vous a aidé à comprendre les CORS ? Vous avez d'autres questions sur la sécurité web ?

💼 LinkedIn 🐙 GitHub 📧 Email

Article rédigé par François MENGUY · Février 2026