Proxys in Node.js verwenden: Axios und node-fetch

Veröffentlicht:

12 Min. Lesezeit

Acar Diveroli
Autor: Acar Diveroli
Ein Node-Würfel, verbunden mit axios- und fetch-Karten, die zu einem Proxy-Knoten führen

In Python ist ein Proxy in den meisten Bibliotheken ein einziger Parameter proxies. In Node.js ist es unübersichtlicher: Das integrierte fetch, Axios und node-fetch nehmen einen Proxy über drei verschiedene Mechanismen entgegen, SOCKS5 braucht ein eigenes Paket, und wenn Sie einer Bibliothek die falsche Option übergeben, erhalten Sie oft nicht einmal einen Fehler; die Anfrage geht stillschweigend ohne Proxy hinaus. Deshalb ist es wichtig zu wissen, welche Bibliothek welche Option liest.

In diesem Artikel behandeln wir die drei grundlegenden Wege, einen Proxy in Node.js festzulegen (Umgebungsvariable, Agent und Dispatcher), die Nutzung eines Proxys mit Authentifizierung über integriertes fetch, Axios und node-fetch, SOCKS5-Proxys sowie IP-Rotation mit Wiederholungen, jeweils mit lauffähigem Code. Die Beispiele haben wir unter Node.js 24 gegen einen lokalen Test-Proxy ausgeführt, der Benutzername und Passwort verlangt, und dabei die aufgetretene Versionsinkompatibilität sowie die unterschiedliche Darstellung von Authentifizierungsfehlern je Bibliothek festgehalten.

Wege, einen Proxy in Node.js festzulegen

Node.js kennt drei Mechanismen, um eine HTTP-Anfrage über einen Proxy zu senden. Welche Bibliothek Sie nutzen, bestimmt, welcher Mechanismus greift.

  1. Umgebungsvariable. Die Variablen HTTP_PROXY, HTTPS_PROXY und NO_PROXY. Sie leiten ein ganzes Skript ohne Codeänderung über den Proxy, doch nicht jede Bibliothek liest sie.
  2. Agent. Die klassischen Module http und https von Node.js bauen Verbindungen über ein Agent-Objekt auf. Axios und node-fetch nutzen diese Module; geben Sie ihnen einen Agent, der sich mit einem Proxy verbindet (etwa HttpsProxyAgent), laufen Anfragen über den Proxy.
  3. Dispatcher. Das integrierte fetch von Node.js nutzt nicht das klassische http-Modul, sondern einen Client namens undici. In undici heißt das Objekt, das Verbindungen verwaltet, dispatcher, und für einen Proxy übergeben Sie einen ProxyAgent.

Diese Unterscheidung ist der wichtigste Punkt des Artikels: Integriertes fetch kennt die Option agent nicht. Schreiben Sie aus Axios-Gewohnheit fetch(url, { agent }), erhalten Sie keinen Fehler, und die Anfrage geht ohne Proxy hinaus.

Wie ein Proxy grundsätzlich funktioniert und welcher CONNECT-Tunnel für HTTPS-Anfragen aufgebaut wird, erklären wir in Was ist ein Proxy-Server und wie funktioniert er?. cURL-Befehle in fetch und Axios zu übersetzen (Header, Body, Formulardaten), ist Thema unseres Artikels cURL in JavaScript; dieser Artikel konzentriert sich nur auf den Proxy.

Wie nutzen Sie einen Proxy mit integriertem fetch?

undici ProxyAgent

Installieren Sie das Paket undici:

bash
npm install undici

Importieren Sie dann fetch und ProxyAgent aus demselben Paket:

javascript
import { fetch, ProxyAgent } from "undici";

const dispatcher = new ProxyAgent("http://user:pass@pr.proxynet.io:8000");

const res = await fetch("https://httpbin.org/ip", {
  dispatcher,
  signal: AbortSignal.timeout(20_000),
});
console.log(res.status, await res.json());

Auch wenn die Proxy-Adresse mit http:// beginnt, erreichen Sie HTTPS-Seiten sicher: ProxyAgent öffnet mit dem Proxy einen CONNECT-Tunnel, und die TLS-Verbindung zum Ziel entsteht innerhalb dieses Tunnels.

Die Falle der Versionsinkompatibilität. Node.js bringt eine eigene undici-Version mit; die aus npm installierte undici-Version kann neuer sein. Als wir den ProxyAgent aus npm an das integrierte fetch von Node.js übergaben (das globale fetch ohne Import), scheiterte die Anfrage unter Node.js 24.11 mit undici 8.10 mit dem Fehler fetch failed; die Ursache war invalid onRequestStart method. Mit dem fetch von undici selbst funktionierte derselbe Agent problemlos. Die Regel ist einfach: Nehmen Sie fetch aus demselben Paket wie ProxyAgent.

Damit alle undici-fetch-Aufrufe der Anwendung denselben Proxy nutzen, können Sie einen globalen Dispatcher festlegen:

javascript
import { fetch, ProxyAgent, setGlobalDispatcher } from "undici";

setGlobalDispatcher(new ProxyAgent("http://user:pass@pr.proxynet.io:8000"));

const res = await fetch("https://httpbin.org/ip"); // kein dispatcher nötig

Die Umgebungsvariable NODE_USE_ENV_PROXY

In aktuellen Node.js-Versionen liest das integrierte fetch die Standard-Proxy-Umgebungsvariablen, wenn NODE_USE_ENV_PROXY aktiviert ist. Laut dem Leitfaden zur Netzwerkkonfiguration in Unternehmen von Node.js ist die Funktion ab den Versionen 22.21.0 und 24.5.0 verfügbar; dasselbe Verhalten lässt sich mit der Befehlszeilenoption --use-env-proxy einschalten.

Unter Linux und macOS:

bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" node app.mjs

In der Windows PowerShell:

powershell
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjs

Diese Methode braucht keine Codeänderung; fetch("https://httpbin.org/ip") läuft direkt über den Proxy. In unserem Test erreichte dieselbe Anfrage ohne NODE_USE_ENV_PROXY den Proxy überhaupt nicht, obwohl HTTPS_PROXY gesetzt war. Interne Netzadressen halten Sie mit der Variablen NO_PROXY vom Proxy fern.

Möchten Sie einen Dispatcher, der Umgebungsvariablen aus dem Code heraus liest, erledigt die undici-Klasse EnvHttpProxyAgent dieselbe Aufgabe:

javascript
import { fetch, EnvHttpProxyAgent } from "undici";

const res = await fetch("https://httpbin.org/ip", { dispatcher: new EnvHttpProxyAgent() });

Wie nutzen Sie einen Proxy mit Axios?

Axios nutzt in Node.js die klassischen Module http und https und bietet zwei Wege für einen Proxy.

Die integrierte proxy-Option

Die Option proxy in der Anfragekonfiguration von Axios funktioniert für unverschlüsselte http://-Adressen:

javascript
import axios from "axios";

const { data } = await axios.get("http://httpbin.org/ip", {
  proxy: {
    protocol: "http",
    host: "pr.proxynet.io",
    port: 8000,
    auth: { username: "user", password: "pass" },
  },
  timeout: 20_000,
});
console.log(data);

Das Passwort im Feld auth wird nicht kodiert; Sonderzeichen können Sie unverändert schreiben.

https-proxy-agent für HTTPS-Adressen

Ist die Zieladresse HTTPS, liefert ein Agent, der den Tunnel aufbaut, zuverlässigere Ergebnisse:

bash
npm install axios https-proxy-agent
javascript
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const httpsAgent = new HttpsProxyAgent("http://user:pass@pr.proxynet.io:8000");

const client = axios.create({
  httpsAgent,
  proxy: false, // schaltet die eigene Proxy-Logik von Axios ab und überlässt den Tunnel dem Agent
  timeout: 20_000,
});

const { status, data } = await client.get("https://httpbin.org/ip");
console.log(status, data);

Lassen Sie die Zeile proxy: false nicht weg. Ist keine proxy-Option gesetzt, versucht Axios womöglich, den Proxy aus Umgebungsvariablen mit eigener Logik anzuwenden, was neben dem Agent zwei widersprüchliche Proxy-Verhalten bedeutet. Binden Sie den Agent einmal mit axios.create, müssen Sie ihn nicht bei jedem Aufruf wiederholen.

Wie nutzen Sie einen Proxy mit node-fetch?

Bevor integriertes fetch kam, war node-fetch die verbreitetste fetch-Implementierung in Node.js, und in älteren Projekten taucht es noch oft auf. Anders als integriertes fetch nutzt es das klassische http-Modul, daher wird der Proxy mit der Option agent übergeben:

bash
npm install node-fetch https-proxy-agent
javascript
import fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://user:pass@pr.proxynet.io:8000");

const res = await fetch("https://httpbin.org/ip", { agent });
console.log(res.status, await res.json());

Version 3 von node-fetch wird nur als ES-Modul veröffentlicht; können Sie es in einem CommonJS-Projekt mit require nicht per import laden, ist der Umstieg auf integriertes fetch meist weniger Aufwand. Für ein neues Projekt gibt es keinen Grund, node-fetch hinzuzufügen.

Wie nutzen Sie einen SOCKS5-Proxy?

Der ProxyAgent von undici ist für HTTP-Proxys gedacht; integriertes fetch hat keine SOCKS5-Unterstützung. Für SOCKS5-Proxys nutzen Sie das Paket socks-proxy-agent mit Axios oder node-fetch:

bash
npm install socks-proxy-agent
javascript
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

// socks5h: Der Domainname wird auf dem Proxy aufgelöst, keine DNS-Abfrage verlässt Ihr Netz
const agent = new SocksProxyAgent("socks5h://user:pass@pr.proxynet.io:1080");

const { data } = await axios.get("https://httpbin.org/ip", {
  httpAgent: agent,
  httpsAgent: agent,
  proxy: false,
});
console.log(data);

In node-fetch wird derselbe Agent als fetch(url, { agent }) übergeben. In den Protokollen unseres Test-Proxys kamen Anfragen mit dem Schema socks5h:// als Domainname beim Proxy an, nicht als IP-Adresse. Das Schema socks5:// löst den Domainnamen auf Ihrem Computer auf; welche Probleme das verursacht, erklären wir in WebRTC- und DNS-Lecks. Bevor Sie zwischen SOCKS5 und einem HTTP-Proxy wählen, lesen Sie SOCKS- und HTTP-Proxy im Vergleich.

Wie nehmen Bibliotheken einen Proxy entgegen?

ClientWie wird der Proxy übergeben?UmgebungsvariableSOCKS5Authentifizierungsfehler
Integriertes fetchNODE_USE_ENV_PROXY oder --use-env-proxyNur mit dem SchalterKeinefetch failed
undici fetchdispatcher: new ProxyAgent(...)Mit EnvHttpProxyAgentKeinefetch failed, Ursache: Anfrage abgebrochen
AxiosOption proxy oder httpsAgent + proxy: falseWenn keine Option gesetzt istMit socks-proxy-agentFehler mit Statuscode 407
node-fetchagentKeineMit socks-proxy-agentHängt vom Agent ab
http.requestagentKeineMit socks-proxy-agentHängt vom Agent ab

Die letzte Spalte ist beim Debuggen wichtig. In unserem Test mit falschem Passwort zeigte Axios die Ursache klar mit der Meldung Request failed with status code 407 und error.response.status === 407. Das fetch von undici gab nur den Fehler fetch failed aus und nannte als Ursache „Request was cancelled"; die Meldung selbst erwähnt die Authentifizierung nicht. Sehen Sie bei undici fetch failed, prüfen Sie zuerst Benutzername und Passwort des Proxys. Alle Ursachen von 407 haben wir in Proxy-Authentifizierung: User:Pass oder IP-Whitelist zusammengestellt.

Zugangsdaten und Sonderzeichen

Bei allen Methoden, die die Proxy-Adresse als URL erwarten (undici ProxyAgent, https-proxy-agent, socks-proxy-agent, Umgebungsvariablen), müssen Zeichen wie @, :, / und # im Passwort kodiert werden. Sonst wird die Adresse falsch zerlegt.

javascript
const user = process.env.PROXY_USER;
const pass = encodeURIComponent(process.env.PROXY_PASS);

const proxyUrl = `http://${user}:${pass}@pr.proxynet.io:8000`;

Die Zugangsdaten aus Umgebungsvariablen zu lesen, statt sie in den Code zu schreiben, verhindert, dass das Passwort überall sichtbar wird, wo der Code geteilt wird. Im Feld proxy.auth von Axios wird das Passwort unkodiert geschrieben.

IP-Rotation und Wiederholungen

Bei einer echten Datenerfassung schlagen Anfragen gelegentlich fehl: Der Ausgangspunkt des Proxys erreicht das Ziel nicht, das Ziel antwortet mit 429 oder 503, oder die Verbindung läuft in ein Timeout. Das folgende Beispiel wählt zufällig zwischen mehreren Proxys, wiederholt eine fehlgeschlagene Anfrage mit exponentiellem Backoff und begrenzt die Zahl gleichzeitig laufender Anfragen durch kleine Stapel:

javascript
import { fetch, ProxyAgent } from "undici";

const PROXIES = [
  "http://user:pass@pr.proxynet.io:8000",
  "http://user:pass@pr.proxynet.io:8001",
];
const agents = PROXIES.map((p) => new ProxyAgent(p));
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function getWithRetry(url, { attempts = 4, baseMs = 500 } = {}) {
  let lastError;
  for (let i = 0; i < attempts; i++) {
    const dispatcher = agents[Math.floor(Math.random() * agents.length)];
    try {
      const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20_000) });
      if (res.status === 429 || res.status >= 500) {
        await res.body?.cancel();
        throw new Error(`HTTP ${res.status}`);
      }
      return await res.text();
    } catch (err) {
      lastError = err;
      await sleep(baseMs * 2 ** i + Math.random() * 250);
    }
  }
  throw lastError;
}

async function crawl(urls, concurrency = 5) {
  const results = [];
  for (let i = 0; i < urls.length; i += concurrency) {
    const batch = urls.slice(i, i + concurrency);
    results.push(...(await Promise.allSettled(batch.map((u) => getWithRetry(u)))));
  }
  return results;
}

const urls = ["https://httpbin.org/ip", "https://httpbin.org/status/503", "https://example.com/"];
const results = await crawl(urls, 2);
results.forEach((r, i) =>
  console.log(urls[i], r.status, r.status === "fulfilled" ? `${r.value.length} Bytes` : r.reason.message),
);

Drei Details im Code sind wichtig:

  • Promise.allSettled verhindert, dass eine fehlgeschlagene Anfrage im Stapel die anderen stoppt. Mit Promise.all würde ein einzelnes 503 die Ergebnisse des ganzen Stapels kosten. Im Beispiel wurde die Adresse /status/503 nach vier Versuchen als rejected markiert, während die anderen Adressen ihre Ergebnisse lieferten.
  • res.body?.cancel() gibt die Verbindung frei, ohne den Body einer Antwort zu lesen, die wir wiederholen werden. Ungelesene Bodys können den Verbindungspool füllen.
  • Die Zufallskomponente (Math.random() * 250) verhindert, dass gleichzeitig fehlgeschlagene Anfragen gleichzeitig wiederholt werden und einen neuen Stau erzeugen.

Statt die Proxy-Liste selbst zu verwalten, können Sie einen Rotierender Proxy nutzen, der über eine einzige Adresse bei jeder Verbindung eine andere Ausgangs-IP liefert; das Array PROXIES schrumpft dann auf ein Element, und die Rotation übernimmt der Anbieter. Für Aufgaben, bei denen die IP über eine Sitzung gleich bleiben muss (angemeldete Seiten, mehrstufige Abläufe), wird ein Sticky-Proxy bevorzugt.

Dieses Beispiel liest den Header Retry-After bei einer 429-Antwort nicht. Einen Ansatz, der den Header berücksichtigt und unterscheidet, bei welchen Statuscodes nicht wiederholt werden sollte, erklären wir in HTTP-Statuscodes beim Web Scraping. Wie sich der Wert für Nebenläufigkeit tatsächlich auf die Geschwindigkeit auswirkt, behandeln wir in Concurrency und Parallelism.

Wie prüfen Sie, ob der Proxy funktioniert?

Prüfen Sie bei jedem neuen Aufbau, ob die Anfrage wirklich über den Proxy läuft. Am einfachsten senden Sie eine Anfrage an eine Adresse, die die Ausgangs-IP zurückgibt, zuerst ohne und dann mit Proxy:

javascript
import { fetch, ProxyAgent } from "undici";

const ip = async (options = {}) => (await (await fetch("https://api.ipify.org?format=json", options)).json()).ip;

console.log("ohne Proxy:", await ip());
console.log("mit Proxy:", await ip({ dispatcher: new ProxyAgent(process.env.HTTPS_PROXY) }));

Zeigen beide Zeilen dieselbe Adresse, läuft die Anfrage nicht über den Proxy. Häufigste Ursache ist ein agent, das an integriertes fetch übergeben wurde, oder ein Agent in Axios ohne proxy: false.

Häufige Fehler

  • Integriertem fetch einen agent übergeben. Die Option wird stillschweigend ignoriert. Der richtige Schlüssel für fetch ist dispatcher.
  • Den ProxyAgent aus npm-undici an das globale fetch übergeben. Passen die Versionen nicht, erhalten Sie fetch failed; importieren Sie fetch ebenfalls aus undici.
  • HTTPS_PROXY setzen und davon ausgehen, dass integriertes fetch es liest. Ohne NODE_USE_ENV_PROXY oder --use-env-proxy liest es sie nicht.
  • In Axios bei einem Agent proxy: false nicht setzen. Zwei Proxy-Mechanismen geraten in Konflikt.
  • Sonderzeichen im Passwort nicht kodieren. Die Adresse wird falsch zerlegt, und die Authentifizierung schlägt fehl.
  • DNS mit socks5:// lokal auflösen. Für die Auflösung auf dem Proxy nutzen Sie socks5h://.
  • Kein Timeout setzen. Ein Proxy-Ausgang, der nicht antwortet, lässt eine Anfrage ohne Timeout minutenlang warten. Nutzen Sie in undici AbortSignal.timeout und in Axios timeout.
  • Alle Anfragen auf einmal mit Promise.all starten. Das belastet Ihren eigenen Verbindungspool ebenso wie das Rate-Limit der Zielseite.

Welche Methode sollten Sie wählen?

Ihre SituationEmpfehlung
Neues Projekt, wenige Abhängigkeitenundici fetch + ProxyAgent
Bestehendes Skript ohne Codeänderung über einen Proxy leitenNODE_USE_ENV_PROXY=1 + HTTPS_PROXY
Das Projekt nutzt bereits AxiosAxios + https-proxy-agent + proxy: false
Älteres Projekt mit node-fetchnode-fetch + agent
SOCKS5-ProxyAxios oder node-fetch + socks-proxy-agent (socks5h://)
Bei jeder Anfrage eine andere IPRotating-Proxy, eine Adresse
Dieselbe IP für die SitzungSticky-Proxy
Klare Fehlermeldungen sind wichtigAxios (zeigt 407 mit Statuscode)

Für JavaScript im Browser gilt all das nicht: Das fetch des Browsers kann den Proxy nicht per Code ändern; der Proxy wird in den Einstellungen des Browsers oder Betriebssystems festgelegt. Die Einrichtung unter Windows beschreibt Proxy-Einstellungen in Windows und Chrome.

Häufig gestellte Fragen

Unterstützt das integrierte fetch von Node.js Proxys?

Ab Node.js 22.21.0 und 24.5.0 liest es die Variablen HTTP_PROXY und HTTPS_PROXY, wenn die Umgebungsvariable NODE_USE_ENV_PROXY=1 oder die Option --use-env-proxy gesetzt ist. Um pro Anfrage aus dem Code einen Proxy anzugeben, nutzen Sie fetch und ProxyAgent aus undici.

Liest Axios die Umgebungsvariable HTTPS_PROXY?

In Node.js versucht Axios, den Proxy aus Umgebungsvariablen zu nutzen, wenn keine proxy-Option gesetzt ist. Für klares, vorhersehbares Verhalten empfehlen wir, den Proxy per Agent festzulegen und proxy: false zu schreiben.

Kann ich für jede Anfrage einen anderen Proxy nutzen?

Ja. In undici können Sie jedem fetch-Aufruf einen anderen ProxyAgent übergeben, in Axios einen anderen httpsAgent. Statt Agents bei jeder Anfrage neu zu erstellen, legen Sie sie wie im Beispiel oben einmal an und verwenden sie wieder; jeder neue Agent öffnet einen eigenen Verbindungspool.

Wie bleiben Cookies bei Proxy-Nutzung erhalten?

Integriertes fetch und undici speichern keine Cookies; Sie müssen den Header Set-Cookie lesen und bei der nächsten Anfrage als Header Cookie mitsenden. In Axios können Sie einen Cookie-Speicher auf Basis von tough-cookie nutzen. Denken Sie daran, dass zusammen mit den Cookies auch die IP-Adresse über die Sitzung gleich bleiben sollte.

Wie übergeben Sie in Puppeteer oder Playwright einen Proxy?

Tools zur Browserautomatisierung nehmen den Proxy nicht wie Node.js-Bibliotheken entgegen, sondern als Option beim Start des Browsers. Die Agents aus diesem Artikel betreffen den Browser nicht. Die Seite von Puppeteer zeigen wir in Puppeteer und CAPTCHA.

Sollte ich JavaScript oder Python wählen?

Proxys funktionieren in beiden Sprachen; in Python nehmen die meisten Bibliotheken einen Proxy mit einem einzigen Parameter entgegen, in Node.js ändert sich der Mechanismus je nach Bibliothek. Die Wahl hängt meist von der Sprache des Teams und der Struktur der Zielseiten ab. Wir vergleichen beide in Web Scraping: JavaScript oder Python?; zu Python-Bibliotheken lesen Sie HTTPX, Requests und AIOHTTP im Vergleich.

Fazit

In Node.js wird ein Proxy je nach Client auf drei Arten übergeben: ein undici-dispatcher oder NODE_USE_ENV_PROXY für integriertes fetch und ein agent für Axios und node-fetch. Nutzen Sie für HTTPS-Ziele Axios mit https-proxy-agent und proxy: false, für SOCKS5 socks-proxy-agent mit dem Schema socks5h://. Nehmen Sie ProxyAgent und fetch aus demselben Paket, kodieren Sie das Passwort, setzen Sie ein Timeout und führen Sie Anfragen in kleinen Promise.allSettled-Stapeln mit Wiederholungslogik aus. Tarife für Ihre Datenerfassung finden Sie auf unserer Seite zur Datenerfassung.

ChatGPT fragenClaude fragen