Retour au blog
Guides
Mihnea-Octavian ManolacheLast updated on May 12, 202618 min read

Axios : les en-têtes en 2026 : le manuel du développeur

Axios : les en-têtes en 2026 : le manuel du développeur
En bref : Axios définit les en-têtes sur cinq niveaux : configuration par requête, valeurs par défaut globales, axios.create() instances, des intercepteurs de requêtes et de réponses, et la réponse elle-même. Ce guide passe en revue chaque couche à l'aide d'extraits de code v1 exécutables, puis corrige les quatre bugs qui posent problème à tout le monde : les limites multipart, les cookies CORS, les certificats auto-signés et la casse des en-têtes.

Axios reste le client HTTP par défaut vers lequel se tournent la plupart des équipes JavaScript et TypeScript, et la plupart des bugs signalés ne sont pas du tout des bugs d'Axios. Ce sont des bugs d'en-tête. Un argument de configuration mal placé supprime silencieusement votre Authorization . Un Content-Type: multipart/form-data corrompt chaque téléchargement. Un axios.defaults jette un jeton à tous les hôtes tiers que vous appelez. Chacun de ces problèmes a une solution simple une fois que vous savez à quel moment du cycle de vie de la requête l'en-tête est censé se trouver.

Ce guide sur les en-têtes Axios est le manuel dont j’aurais aimé disposer lorsque j’ai mis en place ma première pile d’intercepteurs. Il cible Axios v1 sur Node 20+, mais les modèles s’appliquent au navigateur là où ils diffèrent. Vous découvrirez côte à côte les en-têtes par requête, les valeurs par défaut globales, les instances à portée limitée et les intercepteurs de requêtes et de réponses, avec une règle de décision pour choisir entre eux. Vous bénéficierez également d’une section complète sur les en-têtes de réponse Axios que la plupart des tutoriels omettent, ainsi que d’une section de dépannage pour les bugs que vous rencontrez réellement en production.

Configurer Axios v1 dans Node.js et le navigateur

Avant que l'un des modèles ci-dessous ne se comporte comme le prétend la documentation, fixez votre version. Cet article cible Axios v1.x sur Node 20 ou une version ultérieure. La version 0.x normalisait les en-têtes différemment, de sorte que les modèles v1 ne se traduiront pas tous parfaitement.

Installez via n'importe quel gestionnaire de paquets :

npm install axios
# or: yarn add axios   |   pnpm add axios

Projet ESM :

import axios from "axios";

Service CommonJS :

const axios = require("axios");

Page de navigateur standard :

<script src="https://cdn.jsdelivr.net/npm/axios@1/dist/axios.min.js"></script>

Un détail d'environnement qui reviendra plus tard : dans Node, Axios utilise la pile intégrée http/https , ce qui vous permet de contrôler le TLS, les agents et les proxys. Dans le navigateur, il utilise XMLHttpRequest, de sorte que le navigateur applique les règles CORS et SameSite , quelle que soit votre configuration. Si vous avez également besoin d’une couche proxy, un guide dédié au proxy Axios couvre ce câblage de bout en bout.

Transmettez les en-têtes par requête avec l'objet de configuration

Les en-têtes par requête constituent le moyen le plus direct pour Axios de définir des en-têtes pour un appel unique. Ils constituent la valeur par défaut idéale pour l'authentification ponctuelle, la négociation de contenu et le débogage isolé. Rien n'est partagé entre les appels, rien ne se propage vers d'autres modules, et un en-tête défini de cette manière prévaut sur toutes les couches par défaut situées en dessous.

// GET with auth and a custom Accept
const orders = await axios.get("https://api.example.com/orders", {
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: "application/vnd.example.v2+json",
  },
});

// POST: body in arg 2, config in arg 3
const created = await axios.post(
  "https://api.example.com/orders",
  { sku: "ABC-123", quantity: 2 },
  { headers: { "Idempotency-Key": crypto.randomUUID() } }
);

// PUT and PATCH follow the same three-argument shape as POST
await axios.put(url, body, { headers: { "If-Match": etag } });
await axios.patch(url, body, {
  headers: { "Content-Type": "application/merge-patch+json" },
});

// DELETE takes config in arg 2, like GET
await axios.delete(url, {
  headers: { Authorization: `Bearer ${token}` },
});

Quelques règles à retenir. L'objet headers n'accepte que des valeurs de type chaîne de caractères dans la v1 ; les tableaux et null ne sont pas pris en charge. Tout ce que vous définissez par requête est définitif, même si un intercepteur de requête tente de le remplacer ultérieurement (le modèle « guard » est présenté ci-dessous). Et cet exemple d'en-têtes Axios est la manière la plus propre de faire en sorte qu'un appel spécifique ignore tout ce qui est global, puisque les clés par requête l'emportent sur toutes les autres couches.

Le piège des emplacements d'arguments : GET/DELETE vs POST/PUT/PATCH

Le bug Axios le plus souvent signalé par les débutants n’est pas du tout un bug d’en-tête, mais un bug d’emplacement d’argument. axios.get et axios.delete acceptent la configuration comme deuxième argument car ils n'ont pas de corps. axios.post, axios.put, et axios.patch acceptent la configuration comme troisième argument car le corps se trouve dans le deuxième argument.

Si vous vous trompez, vos en-têtes se retrouvent dans le corps. Le serveur ne voit pas Authorization, renvoie un 401, et vous passez une heure à blâmer votre magasin de jetons.

// WRONG: headers object treated as the POST body
await axios.post("https://api.example.com/orders", {
  headers: { Authorization: `Bearer ${token}` }, // becomes the request body
});

// RIGHT: empty body, headers in the third slot
await axios.post(
  "https://api.example.com/orders",
  {},                                                // body
  { headers: { Authorization: `Bearer ${token}` } }  // config
);

Le moyen le plus rapide de confirmer qu'un 401 est dû à ce mauvais placement est de consigner error.config.data et de vérifier si vos en-têtes se sont glissés dans le corps. Si c'est le cas, déplacez-les d'une position vers la droite. Il s'agit de l'erreur POST d'en-têtes Axios par excellence, et elle disparaît une fois que vous avez assimilé les règles de positionnement.

Définissez des valeurs par défaut globales pour chaque requête

Les en-têtes globaux d'Axios sont définis sur axios.defaults.headers et s’appliquent à chaque appel effectué via l’instance par défaut. Ils constituent l’outil idéal pour un ensemble restreint de cas : un script qui communique avec une seule API, un outil de développement interne, une interface en ligne de commande (CLI) ou une petite application qui intègre son propre client API. Dès que vous appelez plus d’un hôte, les valeurs par défaut deviennent un handicap.

// Apply to every method
axios.defaults.headers.common["Accept"] = "application/json";
axios.defaults.headers.common["X-App-Version"] = "2026.04.1";

// Pull secrets from env, never inline
axios.defaults.baseURL = process.env.API_URL;
axios.defaults.headers.common["Authorization"] =
  `Bearer ${process.env.API_TOKEN}`;

Le common bucket est généralement celui que vous souhaitez utiliser, car il s'applique quelle que soit la méthode HTTP. Tout ce que vous y définissez apparaît sur chaque appel sortant GET, POST, PUT, PATCH, DELETE et OPTIONS provenant du singleton Axios par défaut.

Le coût de maintenance apparaît plus tard. Dès qu’un deuxième service ou un SDK tiers s’installe dans la même base de code, votre en-tête global Authorization se retrouve sur des hôtes qu’il n’a pas à toucher. La section « instances » ci-dessous permet de résoudre ce problème de manière adéquate.

Valeurs par défaut spécifiques à chaque méthode (post, get, put)

Axios expose également des groupes de paramètres spécifiques à chaque méthode, tels que axios.defaults.headers.post, .get, .put, .patch, et .delete. Celles-ci ne s’appliquent qu’à ce verbe spécifique, ce qui est parfois utile lorsque vous souhaitez une valeur par défaut différente pour chaque méthode.

// Force a JSON content type only on writes
axios.defaults.headers.post["Content-Type"] = "application/json";
axios.defaults.headers.put["Content-Type"] = "application/json";
axios.defaults.headers.patch["Content-Type"] = "application/json";

Pour tout le reste, utilisez d'abord common , puis ne restreignez-vous à un ensemble de méthodes que lorsque vous avez une raison valable de le faire. En pratique, les ensembles de méthodes ne sont qu'une connaissance de référence pour la plupart des équipes. Les instances constituent presque toujours la solution la plus propre, et c'est dans la section suivante qu'Axios cesse d'être votre outil principal pour les en-têtes par défaut.

Isolez les ensembles d'en-têtes par API à l'aide d'instances axios.create()

axios.create() est la recommandation par défaut pour 2026. Vous créez un petit client par service en amont, définissez ses propres baseURL et ses en-têtes, puis vous transmettez ce client aux modules qui en ont besoin. Chaque instance est indépendante, de sorte que la mise à jour de l'une n'affecte pas les autres.

// Internal API: short tokens, custom version header
export const internalApi = axios.create({
  baseURL: "https://internal.example.com/api",
  headers: {
    "Accept": "application/json",
    "X-Internal-Client": "checkout-svc",
  },
});

// Third-party API: completely separate auth and content negotiation
export const partnerApi = axios.create({
  baseURL: "https://partner.example.com",
  headers: {
    "Accept": "application/vnd.partner.v3+json",
    "X-Partner-Key": process.env.PARTNER_KEY,
  },
});

Vous définissez les en-têtes par instance de la même manière que vous définissez les variables globales, simplement sur l'objet instance :

// After login, write the new token to one instance only
internalApi.defaults.headers.common["Authorization"] = `Bearer ${jwt}`;

Ce modèle vous apporte deux avantages. Votre Authorization en-tête ne s'envoie jamais qu'à l'hôte qui a émis le jeton. Et lorsque vous remplacez un service en amont par un autre, vous modifiez un seul fichier client et rien d'autre. L'idiome des en-têtes axios.create est ce qui permet une évolutivité sans pièges.

Pourquoi une valeur par défaut partagée pour l'autorisation constitue un risque de sécurité

Lorsque vous définissez axios.defaults.headers.common.Authorization = "Bearer ...", chaque appel passant par le singleton Axios par défaut envoie ce jeton, y compris les SDK tiers et les scripts ad hoc qui importent le module axios module. Cela inclut des hôtes qui n’ont aucune idée de ce qu’est votre JWT et qui pourraient l’enregistrer.

L'en-tête d'autorisation Axios doit toujours être limité à l'hôte qui l'a émis. Deux modèles fiables :

// 1. Use a dedicated instance and never touch axios.defaults
internalApi.defaults.headers.common.Authorization = `Bearer ${jwt}`;

// 2. Or guard a request interceptor by baseURL / hostname
axios.interceptors.request.use((cfg) => {
  const host = new URL(cfg.baseURL || cfg.url, "http://x").hostname;
  if (host === "internal.example.com") {
    cfg.headers.Authorization = `Bearer ${jwt}`;
  }
  return cfg;
});

Le modèle d'intercepteur convient aux monorepos qui partagent un axios. Le modèle d’instance est plus propre dans tous les autres cas.

Injecter automatiquement des jetons avec des intercepteurs de requêtes

Un intercepteur de requête est l'outil de définition d'en-têtes d'Axios auquel vous avez recours lorsque l'en-tête doit être calculé au moment de l'appel, et non lors du chargement du module. Il s'exécute sur chaque requête sortante avant qu'elle ne quitte Axios. C'est là que le modèle d'intercepteur de requête Axios prend tout son sens : lisez le jeton actuel où qu'il se trouve, joignez-le à config.headers, renvoyer la configuration modifiée.

Deux types de stockage couvrent la plupart des applications. Dans le navigateur, vous lisez généralement depuis localStorage, sessionStorageou de la mémoire. Dans Node, vous disposez d’un petit module de stockage de jetons auquel le reste de l’application fait appel.

// Browser: read the latest token on every request
internalApi.interceptors.request.use((config) => {
  const token = localStorage.getItem("access_token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
// Node: module-level store, swap-able for Redis or a vault
import { getToken } from "./auth/token-store.js";

internalApi.interceptors.request.use(async (config) => {
  const token = await getToken();
  if (token && !config.headers.Authorization) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

Cet if (!config.headers.Authorization) garde est importante. C'est également le modèle qui permet à une redéfinition par requête de l'emporter sur l'intercepteur lorsque vous en avez besoin, ce que la section sur l'ordre de fusion aborde en détail.

Les intercepteurs gèrent également la logique conditionnelle que les couches par requête et par défaut ne peuvent pas exprimer clairement. Les en-têtes basés sur les rôles, les commutateurs d'environnement et les comportements sensibles à l'URL relèvent tous de cette catégorie :

internalApi.interceptors.request.use((config) => {
  const user = getCurrentUser();
  if (user?.role === "admin") {
    config.headers["X-Admin"] = "true";
  }
  if (config.url?.startsWith("/billing/")) {
    config.headers["X-Sensitive-Path"] = "1";
  }
  if (process.env.NODE_ENV === "development") {
    config.headers["X-Debug-Trace"] = crypto.randomUUID();
  }
  return config;
});

Privilégiez un intercepteur par fonctionnalité lorsque c’est possible. Mélanger l’authentification, les indicateurs de rôle et l’observabilité dans une seule fonction rend le débogage fastidieux, et chaque intercepteur enregistré ne s’exécute de toute façon que dans l’ordre d’enregistrement.

Actualisez les jetons expirés sur 401 avec un intercepteur de réponse

L'actualisation des jetons est le point où la plupart des bases de code Axios accumulent des bugs subtils : boucles de réessai infinies, conditions de concurrence entre requêtes parallèles et appels d'actualisation réécrits par l'intercepteur même censé les actualiser. Le modèle ci-dessous évite ces trois problèmes.

Il s'agit d'un intercepteur de réponse qui intercepte un 401, appelle un point de terminaison de rafraîchissement avec un client Axios distinct, marque la configuration d'origine pour qu'elle ne puisse pas réessayer indéfiniment, réessaie une fois, puis se résigne à la déconnexion en cas d'échec.

import axios from "axios";

// 1. Plain client for refresh, with NO interceptors of its own.
//    This is what keeps the refresh call out of the retry loop.
const authClient = axios.create({ baseURL: "https://auth.example.com" });

// 2. Your normal API client with auth wiring.
const api = axios.create({ baseURL: "https://api.example.com" });

api.interceptors.request.use((cfg) => {
  const t = tokenStore.access;
  if (t) cfg.headers.Authorization = `Bearer ${t}`;
  return cfg;
});

api.interceptors.response.use(
  (res) => res,
  async (error) => {
    const original = error.config;
    if (
      error.response?.status !== 401 ||
      original._retry ||
      !tokenStore.refresh
    ) {
      // Either not an auth failure, already retried once,
      // or we have nothing to refresh with.
      return Promise.reject(error);
    }
    original._retry = true; // 3. one shot only, no loops
    try {
      const { data } = await authClient.post("/refresh", {
        refresh_token: tokenStore.refresh,
      });
      tokenStore.access = data.access;
      tokenStore.refresh = data.refresh;
      // 4. write the new token onto the original request and replay it
      original.headers.Authorization = `Bearer ${data.access}`;
      return api(original);
    } catch (refreshErr) {
      tokenStore.clear();
      window.location.href = "/login"; // or your auth flow
      return Promise.reject(refreshErr);
    }
  }
);

Quelques remarques de production avant que cela ne soit soumis à un trafic réel. Partagez le magasin de jetons entre les onglets (un BroadcastChannel écouteur fonctionne dans les navigateurs) afin qu’un rafraîchissement dans un onglet n’en bloque pas un autre. Enveloppez le rafraîchissement en cours dans un singleton Promise afin que dix erreurs 401 parallèles déclenchent un seul rafraîchissement, et non dix. Et limitez les tentatives au-delà _retry si le point de terminaison de rafraîchissement peut lui-même renvoyer un 401, sinon vous tournez en boucle sur une session révoquée.

Lire les en-têtes de réponse : limites de débit, ETag et pagination

La plupart des guides sur la configuration des en-têtes Axios s’arrêtent au niveau de la requête et ne tiennent jamais compte de ce qui revient. C’est une erreur. L’objet des en-têtes de réponse Axios est l’endroit où l’API vous indique comment agir ensuite : combien de requêtes il vous reste, si votre copie en cache est toujours valide, et où se trouve la page suivante. Ignorez-le, et votre scraper aura l’air bien élevé le premier jour, mais sera limité dès le troisième.

Axios expose les en-têtes de réponse sur response.headers. Les clés sont en minuscules, quelle que soit la casse utilisée par le serveur lors de la transmission. C'est la surprise la plus courante lorsque vous commencez à les lire. Le guide MDN sur les requêtes conditionnelles HTTP couvre la spécification pour ETag et If-None-Match.

const res = await api.get("/orders");

// All read as lowercase keys, regardless of server casing
const limit = Number(res.headers["x-ratelimit-limit"]);
const remaining = Number(res.headers["x-ratelimit-remaining"]);
const resetAt = new Date(Number(res.headers["x-ratelimit-reset"]) * 1000);

console.log(`API budget: ${remaining}/${limit}, resets at ${resetAt.toISOString()}`);

Pour un polling compatible avec la mise en cache, stockez le ETag et relancez-la via If-None-Match. Si la ressource n'a pas changé, le serveur renvoie un 304 avec un corps vide et votre code ignore l'analyse :

let cachedEtag = null;
let cachedBody = null;

async function pollOrders() {
  try {
    const res = await api.get("/orders", {
      headers: cachedEtag ? { "If-None-Match": cachedEtag } : {},
      validateStatus: (s) => s === 200 || s === 304, // 304 is not an error
    });
    if (res.status === 200) {
      cachedEtag = res.headers["etag"];
      cachedBody = res.data;
    }
    return cachedBody;
  } catch (err) {
    /* network/HTTP errors only land here now */
  }
}

Les API paginées utilisent fréquemment l'en-tête Link en-tête (RFC 8288) avec rel="next" et rel="prev". Parcourez-le manuellement ou utilisez un analyseur :

function nextLink(linkHeader) {
  if (!linkHeader) return null;
  const match = linkHeader.split(",").find((p) => /rel="next"/.test(p));
  return match ? match.match(/<([^>]+)>/)[1] : null;
}

let url = "/orders?per_page=100";
while (url) {
  const res = await api.get(url);
  process(res.data);
  url = nextLink(res.headers["link"]);
}

En-têtes à surveiller sur la plupart des API :

En-tête

Ce qu'il vous indique

x-ratelimit-remaining, x-ratelimit-reset

À quel point vous êtes proche d'un 429 et quand le quota se réinitialise

retry-after

Nombre de secondes ou date HTTP à attendre après un 429 ou un 503

etag, last-modified

Validateurs de cache pour les requêtes GET conditionnelles

link

Curseurs de pagination selon la RFC 8288

content-type

Si le corps est au format JSON, XML, NDJSON ou HTML

Si votre code ne vérifie que response.data, vous passez à côté de la moitié de ce que l'API essaie de vous dire.

Comment Axios fusionne les en-têtes : règles de priorité en 2026

Chaque fois que vous définissez des en-têtes Axios à partir de plusieurs couches, la bibliothèque doit les fusionner. Les en-têtes sont calculés au moment de la requête en parcourant cinq couches et en appliquant le principe « last-write-wins ». Connaître l'ordre vous permet de prédire quel en-tête votre serveur voit réellement.

L'ordre de fusion, de la priorité la plus basse à la plus élevée :

  1. Valeurs par défaut de la bibliothèque. Valeurs intégrées à Axios telles que Accept: application/json, text/plain, */* et l'en-tête Content-Type pour les requêtes d'écriture.
  2. Valeurs par défaut globales. Tout ce que vous placez sur axios.defaults.headers.common, .post, .getet ses dérivés.
  3. Valeurs par défaut de l'instance. En-têtes définis sur un client créé avec axios.create({ headers: ... }) ou attribués instance.defaults.headers.* par la suite.
  4. Configuration par requête. L' headers objet que vous transmettez directement à axios.get(url, { headers }), axios.post(url, body, { headers }), etc.
  5. Intercepteurs de requête. Tout ce qu’un intercepteur écrit dans config.headers avant que la requête ne quitte Axios. Ceux-ci s’exécutent après la fusion par requête.

Comme les intercepteurs s'exécutent en dernier, ils prennent techniquement le pas sur les en-têtes par requête. Cela pose problème à presque tout le monde la première fois. La solution consiste à ajouter une condition de contrôle à l'intérieur de l'intercepteur qui vérifie la valeur existante :

api.interceptors.request.use((config) => {
  // Only inject if the caller has not already set Authorization
  if (!config.headers.Authorization) {
    const t = tokenStore.access;
    if (t) config.headers.Authorization = `Bearer ${t}`;
  }
  return config;
});

Avec cette condition en place, un Authorization survit à la pile d'intercepteurs et votre redéfinition se comporte de manière intuitive. C'est le même modèle qui permet à un appel spécifique d'utiliser un jeton de service à service tandis que le reste de l'application utilise un jeton utilisateur.

Corrigez les bugs d'en-tête Axios les plus courants

La plupart des problèmes d'en-tête en production se résument à quatre schémas : une limite multipart supprimée lors des téléchargements, une incompatibilité CORS ou withCredentials incompatibilité dans le navigateur, un certificat TLS auto-signé dans Node et des backends sensibles à la casse qui rejettent le format de transmission envoyé par Axios. Les quatre sous-sections ci-dessous sont rédigées de manière à ce que vous puissiez accéder directement à l'une d'entre elles via la recherche et appliquer la correction sans avoir à revenir en arrière.

Téléchargements multipart/form-data défaillants (le piège de la limite)

Le bug le plus courant concernant le type de contenu FormData d'Axios est la « correction » que les gens appliquent par instinct : définir Content-Type: multipart/form-data eux-mêmes. Cette valeur seule est incomplète, car les corps multipart réels nécessitent un boundary paramètre que le serveur utilise pour séparer les champs. Lorsque vous codez en dur la valeur nue, Axios ne la remplace pas, la délimitation réelle n'atteint jamais le réseau, et le serveur rejette le téléchargement.

La bonne approche consiste à laisser le runtime construire l'en-tête pour vous.

// Node: form-data library exposes getHeaders() with the boundary baked in
import FormData from "form-data";
import fs from "node:fs";

const form = new FormData();
form.append("file", fs.createReadStream("./invoice.pdf"));
form.append("note", "Q4 invoice");

await api.post("/uploads", form, {
  headers: form.getHeaders(), // multipart/form-data; boundary=...
});
// Browser: send a real FormData and let Axios omit Content-Type
const fd = new FormData();
fd.append("file", fileInput.files[0]);
fd.append("note", "Q4 invoice");

await api.post("/uploads", fd); // do not set Content-Type

Une exception : si vous passez un objet brut à Axios et que vous le laissez se sérialiser en FormData en interne, un Content-Type: multipart/form-data convient, car Axios remplira lui-même la délimitation.

Blocages CORS, withCredentials et cookies manquants

CORS est propre au navigateur. Node ne dispose ni de CORS, ni d’en-têtes bloquées, ni de pré-vérification ; cette section entière ne concerne donc que le débogage dans un onglet de navigateur. Les en-têtes CORS d’Axios ne peuvent pas être désactivées depuis le client ; le navigateur détient la règle et Axios s’y conforme. La référence canonique concernant ce que le navigateur impose est le guide CORS de MDN.

Pour que les cookies inter-origines puissent circuler, quatre conditions doivent être réunies :

  1. Le client active les cookies Axios withCredentials. Soit instance.defaults.withCredentials = true, ou { withCredentials: true } par requête.
  2. Le serveur renvoie Access-Control-Allow-Credentials: true.
  3. Access-Control-Allow-Origin une origine spécifique, jamais *. Le caractère générique est rejeté dès lors que des identifiants sont en jeu.
  4. Les cookies sont émis avec SameSite=None; Secure. Un SameSite=Strict cookie ne sera jamais envoyé d'un site à l'autre, même avec withCredentials set.
const api = axios.create({
  baseURL: "https://api.example.com",
  withCredentials: true, // tells the BROWSER to attach cookies
});

Si vous ne possédez pas l'API en amont et ne pouvez pas corriger la réponse CORS, la seule solution honnête consiste à mettre en place un petit proxy côté serveur sur votre propre domaine qui transfère l'appel. Le placer derrière votre propre sous-domaine permet également de contourner le problème de la dépréciation des cookies tiers. Il est utile de garder en favoris un guide d'introduction sur le comportement sous-jacent des cookies.

Certificats HTTPS auto-signés dans Node.js

Dans Node, vous avez le contrôle total sur le TLS, vous pouvez donc communiquer avec une API de développement utilisant un certificat auto-signé sans que le runtime ne vous le signale. Dans un navigateur, cela n’est pas possible et vous ne devriez pas essayer ; l’utilisateur doit faire confiance à l’autorité de certification (CA) du système d’exploitation ou du magasin de certificats du navigateur.

Pour le développement ou la préproduction côté Node uniquement, associez un agent HTTPS personnalisé. La documentation de Node.js sur l'https.Agente couvre toutes les options :

import https from "node:https";

const devApi = axios.create({
  baseURL: "https://dev.internal.local",
  httpsAgent: new https.Agent({ rejectUnauthorized: false }),
});

Ce drapeau désactive la vérification des certificats, ce qui signifie que les attaques de type « man-in-the-middle » ne sont plus détectables. Ne déployez jamais cela en production. La solution en production consiste à ajouter votre autorité de certification au magasin de confiance du système, ou à émettre un véritable certificat (Let's Encrypt, un serveur ACME interne ou le gestionnaire de certificats de votre fournisseur de cloud). rejectUnauthorized: false Il s'agit d'un outil pour la boucle de développement, et non d'une stratégie de déploiement.

Majuscules/minuscules des en-têtes sur les backends hérités pointilleux

En théorie, HTTP ne fait pas la distinction entre majuscules et minuscules, mais un nombre non négligeable de backends hérités refuseront une requête dont les noms d'en-têtes ne respectent pas la casse mixte canonique. Axios normalise certains noms d'en-têtes en interne, mais pour les en-têtes personnalisées, vous devez envoyer la casse attendue par l'upstream.

// Safer than x-api-key on older middleware
await api.get("/secret", {
  headers: { "X-API-Key": process.env.API_KEY },
});

Le revers de la médaille : lorsque vous relisez ces mêmes en-têtes depuis response.headers, Axios met toutes les clés en minuscules. Vous envoyez donc X-API-Key mais lisez res.headers["x-api-key"]. Cette asymétrie est intentionnelle et cohérente dans toute la version 1. Choisissez la casse canonique pour les envois, acceptez les minuscules pour les réceptions, et vous n'aurez plus jamais à déboguer un « en-tête manquant ».

Envoyez des en-têtes de type navigateur pour le web scraping avec Axios

Les serveurs web inspectent les en-têtes de requête pour déterminer si le client est un vrai navigateur ou un bot, et un agent utilisateur Node par défaut comme axios/1.7.7 est le moyen le plus rapide de se faire repérer. Si vous utilisez Axios pour le scraping, envoyez un ensemble d’en-têtes crédible et faites-le tourner.

const UA_POOL = [
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15",
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
];

const scraper = axios.create({
  headers: {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Upgrade-Insecure-Requests": "1",
  },
  timeout: 15_000,
});

// Axios User-Agent rotation per request
scraper.interceptors.request.use((cfg) => {
  cfg.headers["User-Agent"] = UA_POOL[Math.floor(Math.random() * UA_POOL.length)];
  return cfg;
});

Cela vous permet de contourner les filtres anti-bot basiques. Les cibles sophistiquées superposent des empreintes JA3, des profils TLS, des défis Cloudflare et des vérifications comportementales, et aucun ensemble d’en-têtes ne suffit à lui seul à les contourner. Lorsque axios la rotation ne suffit plus, passez à un pool de proxys résidentiels ou à une API de scraping gérée qui gère pour vous les empreintes digitales, les tentatives de reconnexion et les workflows CAPTCHA. Une analyse des raisons pour lesquelles les scrapers sont bloqués au-delà des en-têtes constitue une lecture utile à suivre.

Points clés à retenir pour les en-têtes Axios en 2026

L'ensemble du modèle d'en-têtes d'Axios se résume à une représentation mentale en cinq couches, plus une règle d'ordre :

  • Configuration par requête pour les cas ponctuels et les remplacements ; prévaut sur toutes les valeurs par défaut ci-dessous.
  • Paramètres par défaut globaux réservés aux scripts à API unique et aux outils de développement ; risqués dès lors que vous appelez plusieurs hôtes.
  • axios.create() Instances comme valeur par défaut en 2026 pour toute application communiquant avec plus d'un en amont.
  • Intercepteurs de requêtes pour les préoccupations transversales telles que l'injection d'authentification, les indicateurs de rôle et le traçage.
  • Inspection des réponses afin que response.headers d'influencer réellement votre logique de réessai, de mise en cache et de pagination.

Ordre de fusion, du plus bas au plus élevé : valeurs par défaut de la bibliothèque, valeurs par défaut globales, valeurs par défaut de l'instance, configuration par requête, intercepteurs de requêtes. Utilisez la if (!config.headers.x) guard à l'intérieur des intercepteurs lorsque vous souhaitez que la configuration par requête prévale.

FAQ

Quelle est la différence entre axios.defaults.headers.common et axios.defaults.headers.post ?

common s'applique à toutes les méthodes HTTP de l'instance Axios par défaut ; les en-têtes qui y sont définis s'appliquent donc aux requêtes GET, POST, PUT, PATCH et DELETE. post (et .get, .put, .patch, .delete) ne s'applique que lorsque ce verbe spécifique est utilisé. Privilégiez common ; n'utilisez les groupes de méthodes que lorsqu'un verbe nécessite réellement une valeur par défaut différente.

Comment faire en sorte qu'un en-tête par requête l'emporte sur un intercepteur de requête dans Axios ?

Protégez l'intercepteur de manière à ce qu'il n'écrive un en-tête que si l'appelant n'en a pas déjà défini un. Le modèle est if (!config.headers.Authorization) { config.headers.Authorization = ... }. Comme les intercepteurs s'exécutent après la fusion par requête, une affectation inconditionnelle écrasera votre redéfinition. La version protégée respecte ce que l'appelant a transmis.

Pourquoi Axios met-il les noms d'en-têtes en minuscules lorsque je les lis dans response.headers ?

Axios normalise les clés d'en-tête de réponse en minuscules par convention. HTTP lui-même traite les noms d'en-tête sans distinction de casse, et la conversion en minuscules lors de la lecture signifie que vous n'avez jamais à deviner la casse pour etag, content-type, ou x-ratelimit-remaining. Envoyez la casse canonique en sortie, lisez en minuscules à l'entrée.

Axios peut-il contourner CORS lors de l'appel d'une API tierce depuis le navigateur ?

Non. Le CORS est appliqué par le navigateur avant même qu'Axios ne s'exécute, il n'existe donc aucun indicateur côté client permettant de le désactiver. La seule véritable solution consiste à demander au serveur en amont d'envoyer le bon Access-Control-Allow-Origin et Access-Control-Allow-Credentials , ou de faire transiter l'appel par un petit proxy côté serveur que vous contrôlez. Node n'a aucune règle CORS.

Comment envoyer un jeton Bearer uniquement à une baseURL spécifique sans le divulguer à d'autres hôtes ?

Créez une instance axios.create() pour cette API et définissez son Authorization uniquement sur l'instance. N'enregistrez jamais le jeton dans axios.defaults, qui le diffuse à tous les hôtes appelés par le singleton par défaut. Si vous devez utiliser une instance partagée, protégez un intercepteur de requête avec une vérification du nom d’hôte afin que l’en-tête ne soit joint que lorsque l’URL correspond à votre baseURL de confiance.

En conclusion

Si vous ne devez retenir qu’une seule chose de ce guide sur la configuration des en-têtes avec Axios, retenez la hiérarchie : chaque en-tête se trouve à l’un des cinq emplacements suivants, dans l’ordre : valeurs par défaut de la bibliothèque, valeurs par défaut globales, valeurs par défaut de l’instance, configuration par requête, puis intercepteurs de requête. Choisissez la couche la plus élevée qui correspond à la durée de vie de l’en-tête. Les remplacements ponctuels vont par requête. Les préoccupations à l’échelle de l’application vont dans une instance. L'authentification transversale va dans un intercepteur. Et chaque réponse Axios comporte des en-têtes qui devraient influencer votre prochaine décision, et non être ignorées.

Les mêmes principes s’appliquent lorsque vous faites plus qu’appeler des API REST bien comportées. Dès que vous pointez Axios vers une cible qui identifie les clients, limite agressivement le débit ou cache des données derrière des défis JavaScript, la rotation des en-têtes seule ne suffit plus. C'est là qu'une couche gérée devient la solution la plus efficace : l'API WebScrapingAPI Scraper gère la rotation des proxys, les empreintes TLS au niveau du navigateur et la résolution des CAPTCHA derrière un seul point de terminaison, de sorte que le code Axios que vous venez d'écrire conserve sa structure et que vous n'ayez plus à vous occuper des blocages. Ajoutez cette page à vos favoris comme référence pour les en-têtes Axios, et optez pour la voie gérée lorsque la couche de requêtes n'est plus le problème principal.

À propos de l'auteur
Mihnea-Octavian Manolache, Développeur Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDéveloppeur Full Stack

Mihnea-Octavian Manolache est ingénieur Full Stack et DevOps chez WebScrapingAPI, où il développe des fonctionnalités pour les produits et assure la maintenance de l'infrastructure qui garantit le bon fonctionnement de la plateforme.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.