Base de connaissances

Comment utiliser des proxys résidentiels en Node.js avec Axios et Got

Les proxys en Node.js : pourquoi Axios a besoin de https-proxy-agent et proxy:false, l'option agent de Got, le ProxyAgent d'undici pour fetch, et la rotation géo par requête.

Chris Collins

Chris Collins

29 juillet 2026 · 10 min de lecture

Node.js est le runtime par défaut pour une énorme quantité de travail de scraping et d’automatisation : un event loop qui hausse les épaules devant des milliers de requêtes concurrentes, un écosystème de paquets massif, et le même langage de bout en bout. Brancher un proxy résidentiel dedans, c’est quelques lignes, mais les détails font trébucher les gens d’une façon spécifique à Node, parce que le client HTTP le plus populaire, Axios, a une option proxy qui ne fait pas ce que vous attendez en HTTPS. Réussissez l’agent et le reste est facile.

Ceci est l’entrée Node.js de la même série que proxys résidentiels avec Python, avec Playwright, et avec Go : le code qui marche pour Axios, Got et le fetch natif, plus les pièges propres à l’écosystème Node.

Tout ci-dessous utilise le gateway résidentiel de Shifter : un point de terminaison, p.shifter.io:443, avec tout le ciblage encodé dans le nom d’utilisateur. Changez l’hôte et les identifiants pour un autre fournisseur ; la forme est la même.

Le modèle du gateway en un paragraphe

Le nom d’utilisateur du proxy porte votre authentification et votre ciblage. Vous ne changez pas de point de terminaison pour changer de pays ou de session, vous changez la chaîne du nom d’utilisateur :

customer-USERNAME-country-us-sid-abc123-ttl-600

country-us cible les États-Unis, sid fixe une session sticky, ttl maintient cette IP pendant N secondes. Omettez sid/ttl et chaque nouvelle connexion tourne. Le mot de passe est constant. En Node, cette chaîne entière va dans l’URL du proxy que vous passez à un proxy agent.

Axios : utilisez un agent, pas l’option proxy intégrée

Voici la chose la plus importante de ce billet. Axios a une option proxy, et pour les cibles HTTPS avec authentification elle est peu fiable, elle n’ouvre pas de tunnel CONNECT correct et échoue en silence ou fuit votre vraie IP. La solution sur laquelle l’écosystème s’est arrêté est de passer à Axios un httpsAgent construit avec https-proxy-agent, et de poser proxy: false pour qu’Axios n’essaie pas de gérer le proxy lui-même.

import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';
const user = `${process.env.SHIFTER_USER}-country-us`;
const pass = process.env.SHIFTER_PASS;
const proxyUrl = `http://${user}:${pass}@p.shifter.io:443`;
const agent = new HttpsProxyAgent(proxyUrl);
const client = axios.create({
httpsAgent: agent,
proxy: false, // critique: laisse l'agent le gérer, pas Axios
timeout: 30000,
});
const res = await client.get('https://api.ipify.org');
console.log(res.data); // une IP résidentielle américaine

Deux choses à intérioriser. La chaîne user inclut les flags de ciblage (-country-us), parce que c’est là que vit la géo. Et proxy: false n’est pas optionnel, sans lui la propre logique de proxy d’Axios entre en collision avec l’agent et vous obtenez exactement le comportement cassé que vous vouliez éviter. Cette seule ligne est le bug de proxy en Node le plus courant.

Got : l’agent va dans l’emplacement agent.https

Got prend le proxy agent via son option agent, indexée par protocole. Le même https-proxy-agent en dessous, sans la danse du proxy: false parce que Got n’a pas de gestion de proxy intégrée avec laquelle se battre.

import got from 'got';
import { HttpsProxyAgent } from 'https-proxy-agent';
const proxyUrl = `http://${user}:${pass}@p.shifter.io:443`;
const res = await got('https://api.ipify.org', {
agent: { https: new HttpsProxyAgent(proxyUrl) },
timeout: { request: 30000 },
});
console.log(res.body); // une IP résidentielle américaine

Une note d’empaquetage : Got est ESM pur depuis la v12, donc import got from 'got' a besoin d’un projet ESM ("type": "module" dans package.json) ou d’un import() dynamique. Si vous êtes coincé sur CommonJS, restez sur Got v11 ou passez à Axios/undici. Cette division ESM-vs-CommonJS est un obstacle spécifique à Node, pas un problème de proxy, mais elle mord ceux qui montent cela pour la première fois.

Fetch natif : le ProxyAgent d’undici comme dispatcher

Node 18+ livre un fetch global adossé à undici, et undici a son propre support de proxy qui n’utilise pas https-proxy-agent du tout. Vous passez un ProxyAgent comme le dispatcher de la requête :

import { ProxyAgent } from 'undici';
const dispatcher = new ProxyAgent(`http://${user}:${pass}@p.shifter.io:443`);
const res = await fetch('https://api.ipify.org', { dispatcher });
console.log(await res.text()); // une IP résidentielle américaine

Pour router chaque fetch du processus à travers le proxy, posez-le en global :

import { setGlobalDispatcher, ProxyAgent } from 'undici';
setGlobalDispatcher(new ProxyAgent(proxyUrl));

Si vous êtes sur du Node moderne et voulez zéro dépendance de client HTTP, c’est le chemin le plus propre.

Piège 1 : réutilisez l’agent, n’en construisez pas un par requête

Quel que soit le client que vous choisissez, le proxy agent possède le pool de connexions. Construire un HttpsProxyAgent ou ProxyAgent frais à chaque requête jette le keep-alive et paie un handshake TCP + TLS complet à travers le proxy à chaque fois, le surcoût que le guide de latence existe pour éliminer. Construisez l’agent une fois pour une identité donnée et réutilisez-le entre les requêtes. Créez le client Axios/Got (ou le dispatcher undici) au démarrage et gardez-le.

Piège 2 : bornez votre concurrence, l’event loop ne le fera pas pour vous

L’event loop de Node rend trivial de tirer mille requêtes d’un coup, et rien ne vous arrête. await Promise.all(urls.map(fetchOne)) sur un grand tableau ouvrira chaque connexion simultanément, ce qui épuise les sockets de votre côté et ressemble à une attaque pour la cible. Bornez les requêtes en vol avec un petit limiteur de concurrence (p-limit est le choix courant) ou une file simple :

import pLimit from 'p-limit';
const limit = pLimit(8); // au plus 8 requetes en vol
const results = await Promise.all(
urls.map(url => limit(() => client.get(url)))
);

Bornez la concurrence par hôte cible, pas seulement globalement, pour qu’un site fragile ne soit pas martelé pendant qu’un permissif est affamé. Plus de parallélisme au-delà de la tolérance d’une cible achète des blocages, pas du débit (comment éviter de se faire bloquer). Ajustez la limite à ce que chaque hôte tolère.

Piège 3 : les erreurs async ont besoin d’une gestion explicite

Un échec de proxy ou de connexion apparaît comme une promesse rejetée, et un rejet non géré peut faire crasher le processus ou, pire, laisser tomber en silence une tâche d’un lot. Enveloppez chaque requête pour qu’un échec de transport réessaie avec une identité fraîche au lieu d’abattre la course :

async function fetchWithRetry(client, url, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try {
return await client.get(url);
} catch (err) {
if (i === attempts - 1) throw err;
// transitoire (ECONNRESET, timeout, proxy 5xx): recule et reessaie
await new Promise(r => setTimeout(r, 500 * 2 ** i));
}
}
}

Distinguez une connexion cassée qui vaut la peine d’être réessayée d’un ralentissement délibéré ; un timeout est une tentative cassée, un 429 est le serveur qui demande de l’espace et devrait reculer, pas marteler.

Faire tourner géo et sessions

Parce que le ciblage vit dans le nom d’utilisateur, une identité différente est une URL de proxy différente, ce qui signifie un agent différent. Le motif efficace est de mettre en cache un agent par identité pour que vous gardiez le pool de connexions par session au lieu de le reconstruire :

const agents = new Map();
function agentFor(country, sid) {
const key = `${country}:${sid ?? 'rotate'}`;
if (!agents.has(key)) {
const u = `${process.env.SHIFTER_USER}-country-${country}` +
(sid ? `-sid-${sid}-ttl-600` : '');
const url = `http://${u}:${process.env.SHIFTER_PASS}@p.shifter.io:443`;
agents.set(key, new HttpsProxyAgent(url));
}
return agents.get(key);
}
// par requete:
await axios.get(targetUrl, { httpsAgent: agentFor('de', 'job-42'), proxy: false });

Donnez à chaque unité logique de travail son propre sid et tournez entre unités, pas en milieu de flux (sticky vs rotatif couvre la distinction), et mappez le travail aux identités comme le décrit le billet sur la répartition de charge.

Vérifiez que vous êtes bien sur le proxy

Avant de benchmarker ou de déboguer quoi que ce soit d’autre, confirmez l’IP de sortie :

const res = await client.get('http://ip-api.com/json');
console.log(res.data); // attendez une IP résidentielle dans le pays ciblé

Votre propre IP signifie que l’agent n’est pas appliqué (avec Axios, presque toujours un proxy: false manquant). Un blocage signifie que la sortie locale est bloquée. Les deux sont couverts dans le guide de diagnostic des timeouts.

FAQ

Pourquoi l’option proxy d’Axios ne marche-t-elle pas avec mon proxy HTTPS ? L’option proxy intégrée d’Axios ne tunnelise pas HTTPS avec authentification de façon fiable. Utilisez un httpsAgent construit avec https-proxy-agent et posez proxy: false pour qu’Axios cesse d’essayer de le gérer. Cette combinaison est le chemin fiable et corrige le symptôme « il renvoie ma vraie IP ».

Ai-je besoin de https-proxy-agent si j’utilise fetch natif ? Non. Le fetch de Node 18+ est adossé à undici, qui a son propre ProxyAgent que vous passez comme le dispatcher (ou posez en global avec setGlobalDispatcher). https-proxy-agent est pour Axios, Got et les modules intégrés http/https.

Pourquoi import got from 'got' lève-t-il dans mon projet ? Got est ESM pur depuis la v12, donc il a besoin d’un projet ESM ("type": "module") ou d’un import() dynamique. En CommonJS, restez sur Got v11 ou utilisez Axios/undici. C’est une question de système de modules, pas de proxy.

Comment faire tourner les IP par requête en Node.js ? Variez le nom d’utilisateur du proxy, ce qui signifie une URL de proxy différente et un agent différent. Mettez en cache un agent par identité dans une Map pour que chaque session garde son propre pool de connexions, et choisissez l’agent par requête. Omettez le sid dans le nom d’utilisateur pour tourner à chaque nouvelle connexion.

Axios, Got ou fetch pour le scraping ? Les trois marchent. Le fetch natif + undici a zéro dépendance supplémentaire sur du Node moderne ; Got a des réessais et des streams ergonomiques ; Axios est ubiquitaire et familier mais a besoin du correctif proxy: false. Choisissez selon l’ergonomie et les dépendances existantes, la configuration du proxy et les pièges ci-dessus s’appliquent aux trois.

En résumé

Node.js plus proxys résidentiels est rapide à monter dès que vous connaissez la seule règle non évidente : avec Axios, utilisez un https-proxy-agent et posez proxy: false, jamais l’option proxy intégrée ; avec Got, mettez l’agent dans agent.https ; avec fetch natif, passez un ProxyAgent d’undici comme dispatcher. Ensuite réutilisez l’agent pour que les connexions restent chaudes, bornez votre concurrence parce que l’event loop ne le fera pas, gérez les erreurs async pour qu’une mauvaise requête réessaie au lieu de crasher, et variez le nom d’utilisateur du proxy pour changer géo ou session.

Réussissez cela et Node gère la collecte concurrente aussi bien que n’importe quoi. Pointez-le vers le gateway résidentiel, et souvenez-vous que la qualité du pool décide à quelle fréquence vous réessayez tout court (réputation d’IP). La page tarifs propose les forfaits au Go pour le tester contre vos propres cibles.

Prêt à commencer ?

Essayez les proxies résidentiels de Shifter, 205M+ IPs, 195+ pays, à partir de $0.75/GB.

Commencer