Next.js CORS error : corriger Axios et localhost

Next.js CORS error apparaît lorsque le navigateur bloque une requête entre deux origines différentes, par exemple une application Next.js sur http://localhost:3000 et une API sur http://localhost:8000. La correction consiste à autoriser exactement l’origine du front côté API, à gérer la requête préliminaire OPTIONS et à ne jamais résoudre le problème avec mode: no-cors. En développement, un proxy Next.js peut éviter le blocage, mais il ne remplace pas la configuration CORS de production.

CORS signifie Cross Origin Resource Sharing, un mécanisme du navigateur qui contrôle les échanges entre origines. Une origine est définie par le protocole, le domaine et le port. Ainsi, localhost:3000 et localhost:8000 sont deux origines distinctes, même si elles utilisent le même nom d’hôte. Ce guide permet de diagnostiquer l’erreur avec Axios, fetch, l’App Router et plusieurs backends courants.

Next.js CORS error : lire le message avant de modifier le code

Le message affiché dans la console contient souvent la cause réelle. « No Access-Control-Allow-Origin header » signifie que l’API répond sans autoriser l’origine du front. « Response to preflight request doesn’t pass access control check » désigne généralement une requête OPTIONS mal traitée. « Network Error » dans Axios est plus vague : il peut cacher un refus CORS, une API arrêtée, une URL incorrecte ou un certificat local invalide.

Commencez par ouvrir l’onglet Network des outils développeur. Sélectionnez la requête qui échoue, observez son URL, sa méthode et son statut, puis cherchez une requête OPTIONS juste avant l’appel principal. Si aucune réponse n’arrive, contrôlez que le serveur API écoute sur le bon port. Si la réponse arrive mais n’a pas les headers attendus, corrigez le backend, pas seulement le composant React.

Origine du front : http://localhost:3000
Origine de l’API  : http://localhost:8000

À vérifier dans Network :
1. URL finale de la requête
2. Réponse OPTIONS éventuelle
3. Access-Control-Allow-Origin
4. Access-Control-Allow-Methods
5. Access-Control-Allow-Headers
6. Cookies et mode credentials

Une erreur CORS ne signifie pas forcément que l’API est inaccessible. Un appel effectué avec curl peut fonctionner car curl n’applique pas la politique de sécurité du navigateur. Ce test est utile pour séparer un problème réseau d’un problème de headers, mais il ne prouve pas que le front pourra lire la réponse.

Pourquoi Axios déclenche une requête preflight

Le navigateur envoie directement une requête simple seulement si la méthode, les headers et le type de contenu restent dans les catégories autorisées. Un appel JSON avec Content-Type: application/json, un header Authorization ou une méthode comme PUT déclenche souvent une requête preflight OPTIONS. L’API doit répondre à cette étape avant que le navigateur n’envoie la vraie requête.

Le preflight demande au serveur si l’origine, la méthode et les headers sont acceptés. Une réponse 200 ou 204 est généralement adaptée, avec les headers CORS présents. Une redirection vers une page de connexion, une réponse 404 ou une authentification qui rejette OPTIONS provoque l’échec de l’appel principal.

import axios from 'axios';

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  headers: {
    'Content-Type': 'application/json'
  }
});

export async function loadProjects() {
  const response = await api.get('/projects');
  return response.data;
}

Le code Axios n’est pas une autorisation CORS. Il déclenche seulement une requête depuis le navigateur. L’autorisation doit venir du serveur qui possède l’URL appelée. Si vous ajoutez un token Bearer, vérifiez que le backend autorise explicitement le header Authorization et qu’il répond aussi à OPTIONS sans exiger le token applicatif.

Corriger CORS dans une API Node et Express

Avec Express, le paquet cors simplifie la production des headers, mais une configuration trop permissive reste une mauvaise solution. En développement, autorisez l’origine exacte du serveur Next.js. En production, utilisez la liste des domaines réellement utilisés par votre application, sans accepter automatiquement toutes les origines.

import express from 'express';
import cors from 'cors';

const app = express();
const allowedOrigins = [
  'http://localhost:3000',
  'https://app.exemple.fr'
];

app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      return callback(null, true);
    }
    return callback(new Error('Origine CORS refusée'));
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true
}));

app.use(express.json());

Placez le middleware CORS avant les routes et avant une logique qui pourrait terminer la requête. Si un reverse proxy ou un middleware d’authentification intercepte OPTIONS, il doit laisser passer le preflight. Testez également le cas sans header Origin, car les appels serveur à serveur ne suivent pas exactement le même parcours que les appels depuis un navigateur.

Configurer CORS avec FastAPI, Django ou Laravel

Le principe ne change pas avec un backend Python ou PHP : l’API doit autoriser l’origine, la méthode et les headers demandés. Avec FastAPI, utilisez le middleware CORS et indiquez les origines explicitement. Avec Django, une extension maintenue peut gérer les headers, mais la liste des origines doit rester contrôlée. Avec Laravel, configurez les chemins concernés, les méthodes et les origines dans la configuration CORS, puis videz le cache de configuration.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        'http://localhost:3000',
        'https://app.exemple.fr'
    ],
    allow_credentials=True,
    allow_methods=['GET', 'POST', 'PUT', 'DELETE'],
    allow_headers=['Content-Type', 'Authorization'],
)

Ne copiez pas une configuration qui mélange allow_credentials: true et une origine wildcard. Les navigateurs refusent généralement les credentials avec Access-Control-Allow-Origin: *. Pour les cookies, retournez l’origine exacte et ajoutez Access-Control-Allow-Credentials: true. Le cookie lui-même doit aussi avoir des attributs compatibles avec son contexte, notamment SameSite et Secure.

Utiliser un proxy Next.js en développement

Lorsque le front et l’API sont servis sous la même origine publique, un proxy Next.js est pratique pour le développement. Avec l’App Router, vous pouvez créer une route interne qui appelle l’API, ou utiliser les rewrites de next.config.js. Le navigateur appelle alors le domaine Next.js, tandis que le serveur Next.js effectue la requête vers le backend.

/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/backend/:path*',
        destination: 'http://localhost:8000/:path*'
      }
    ];
  }
};

export default nextConfig;

Le composant peut ensuite appeler /api/backend/projects au lieu de l’URL externe. Cette technique ne corrige pas la politique CORS du backend pour les autres clients. Elle masque simplement la différence d’origine pour le navigateur pendant le développement. En production, remplacez l’adresse locale par un service accessible et vérifiez les timeouts, les logs et les règles du proxy.

Une route serveur Next.js peut aussi garder une clé privée hors du navigateur. C’est souvent préférable lorsque l’API exige un secret qui ne doit jamais apparaître dans une variable préfixée NEXT_PUBLIC_. Pour les erreurs de rendu entre serveur et navigateur, consultez notre guide sur le Next.js hydration error.

Éviter les erreurs de variables d’environnement et d’URL

Une URL d’API mal chargée produit parfois une erreur qui ressemble à CORS. Les variables exposées au navigateur doivent commencer par NEXT_PUBLIC_. Après une modification du fichier .env.local, redémarrez le serveur Next.js. Une variable absente peut transformer l’URL en valeur indéfinie, en chemin relatif inattendu ou en adresse de production non autorisée.

# .env.local, développement
NEXT_PUBLIC_API_URL=http://localhost:8000

# .env.production, exemple
NEXT_PUBLIC_API_URL=https://api.exemple.fr

Ne placez pas un secret dans NEXT_PUBLIC_API_URL ou dans un fichier envoyé au navigateur. Inspectez l’URL finale dans Network plutôt que de supposer que la variable a été remplacée. Si l’application utilise Docker, localhost désigne le conteneur courant, pas nécessairement votre machine hôte. Utilisez le nom du service Docker ou une adresse adaptée à l’architecture.

Si l’API répond en HTTPS et le front en HTTP, le navigateur peut aussi bloquer la requête pour contenu mixte. Ce message est différent de CORS, mais le résultat visuel peut être le même. Corrigez d’abord le protocole, le DNS et le certificat, puis revenez aux headers CORS.

Gérer les cookies, les tokens et les credentials

Une API sans session n’a généralement pas besoin de withCredentials. Si vous utilisez une session cookie, activez cette option dans Axios et autorisez les credentials côté serveur. Dans tous les cas, ne l’activez pas par réflexe, car cela impose une configuration CORS plus stricte et peut exposer des données si la liste des origines est mal contrôlée.

const api = axios.create({
  baseURL: 'https://api.exemple.fr',
  withCredentials: true
});

const response = await api.get('/me');

Pour un token Bearer, le token est généralement envoyé dans Authorization. Le serveur doit autoriser ce header, mais il ne doit pas le renvoyer dans une réponse accessible à une origine non prévue. Pour une session cookie, vérifiez le domaine, le chemin, la politique SameSite et la présence de HTTPS. Une réponse 200 sans cookie enregistré indique souvent un problème de configuration du navigateur ou du serveur.

Ne tentez pas de résoudre une erreur en ajoutant mode: 'no-cors'. Le navigateur produit alors une réponse opaque que votre code ne peut pas lire, ce qui masque le problème au lieu de le corriger. Cette option n’est pas un contournement pour une API JSON.

Tester le preflight avec curl

Un test curl permet de reproduire la demande OPTIONS et de voir les headers retournés. Remplacez l’URL et l’origine par celles de votre projet. Le test doit retourner une origine autorisée, les méthodes nécessaires et les headers demandés. Il ne remplace pas un test dans Chrome, mais il rend la configuration du serveur observable.

curl -i -X OPTIONS https://api.exemple.fr/projects 
  -H 'Origin: http://localhost:3000' 
  -H 'Access-Control-Request-Method: GET' 
  -H 'Access-Control-Request-Headers: authorization,content-type'

Si la réponse contient une page HTML de connexion, une erreur 301 ou un 404, corrigez le routage OPTIONS. Si elle contient Access-Control-Allow-Origin mais pas Access-Control-Allow-Headers, ajoutez le header demandé côté API. Si tout semble correct dans curl mais échoue dans le navigateur, comparez exactement l’origine, le protocole, le port et les credentials.

Diagnostic rapide
API arrêtée                     → démarrer le service et vérifier le port
Origin absente de la réponse     → autoriser l’origine exacte
OPTIONS en 401 ou 403            → exclure le preflight de l’authentification
Header Authorization refusé      → compléter allowed headers
Wildcard avec cookies             → remplacer * par l’origine exacte
URL localhost en Docker           → utiliser le nom de service adapté

Différencier CORS d’une erreur serveur

Une erreur 500 de l’API n’est pas une erreur CORS, même si le navigateur affiche une plainte CORS lorsque la réponse d’erreur ne contient pas les headers attendus. Regardez les logs du backend et reproduisez l’appel sans navigateur. Ajoutez les headers CORS aux réponses normales et aux réponses d’erreur si votre framework ne le fait pas automatiquement.

Un statut 404 peut également venir d’un préfixe différent, comme /api/v1 oublié dans l’URL. Un statut 401 peut indiquer un token manquant, tandis qu’un statut 403 peut venir d’une règle d’autorisation métier. Corrigez la cause HTTP avant de modifier la politique du navigateur.

Pour une API WordPress, CORS et authentification REST sont deux sujets liés mais distincts. La configuration doit préserver les permissions et ne pas rendre les endpoints utilisateurs publics. Notre guide sur la sécurisation de l’API REST WordPress détaille ce point.

Checklist de correction Next.js CORS error

Appliquez les vérifications dans l’ordre, du réseau vers le code. Cette méthode évite d’ajouter des headers au hasard et permet de garder une configuration reproductible entre développement, recette et production.

□ Confirmer l’URL, le protocole et le port de l’API
□ Vérifier que l’API répond depuis le réseau attendu
□ Lire la requête OPTIONS dans Network
□ Autoriser l’origine exacte du front
□ Autoriser les méthodes réellement utilisées
□ Autoriser Content-Type et Authorization si nécessaire
□ Traiter OPTIONS avant l’authentification applicative
□ Configurer les credentials seulement si des cookies sont utilisés
□ Redémarrer Next.js après modification de .env.local
□ Tester avec curl puis dans un navigateur neuf
□ Vérifier les réponses 4xx et 5xx dans les logs

Une configuration CORS saine est spécifique, documentée et différente selon l’environnement. Gardez les origines locales dans la configuration de développement, les domaines de recette dans la recette et les domaines de production en production. Ne corrigez pas une erreur locale en ouvrant durablement toutes les origines.

Sources

FAQ : Next.js CORS error

Pourquoi Axios affiche-t-il Network Error avec Next.js ?

Axios affiche souvent Network Error lorsque le navigateur bloque la réponse CORS, mais l’API peut aussi être arrêtée, mal adressée ou inaccessible. Vérifiez la console, l’onglet Network, la requête OPTIONS et les logs du backend avant de modifier Axios.

Comment autoriser localhost:3000 dans une API ?

Ajoutez exactement http://localhost:3000 à la liste des origines autorisées par l’API, puis autorisez les méthodes et headers utilisés. N’ajoutez pas de slash final et ne remplacez pas cette origine par un wildcard si des cookies sont envoyés.

Faut-il utiliser mode no-cors pour corriger CORS ?

Non, mode no-cors ne corrige pas CORS. Il produit une réponse opaque que JavaScript ne peut pas lire. Configurez plutôt l’API pour répondre au preflight et retourner les headers CORS adaptés à l’origine du front.

Pourquoi la requête OPTIONS reçoit-elle une erreur 401 ?

La requête OPTIONS est un preflight automatique et ne porte pas toujours le token attendu par votre authentification. Faites traiter OPTIONS avant le middleware d’authentification applicative, tout en conservant les contrôles sur la vraie requête métier.

Un proxy Next.js remplace-t-il CORS en production ?

Un proxy Next.js peut masquer la différence d’origine pour un appel du navigateur, mais il ne remplace pas une politique CORS correcte pour les autres clients. En production, configurez aussi le backend, le reverse proxy, les credentials et les domaines autorisés.

G
WP Admin Lab

Architecte web full-stack. WordPress, performance, data et sécurité. Notes de terrain, tests reproductibles et retours d'expérience.