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.
- Umgebungsvariable. Die Variablen
HTTP_PROXY,HTTPS_PROXYundNO_PROXY. Sie leiten ein ganzes Skript ohne Codeänderung über den Proxy, doch nicht jede Bibliothek liest sie. - Agent. Die klassischen Module
httpundhttpsvon Node.js bauen Verbindungen über einAgent-Objekt auf. Axios und node-fetch nutzen diese Module; geben Sie ihnen einen Agent, der sich mit einem Proxy verbindet (etwaHttpsProxyAgent), laufen Anfragen über den Proxy. - Dispatcher. Das integrierte
fetchvon Node.js nutzt nicht das klassischehttp-Modul, sondern einen Client namens undici. In undici heißt das Objekt, das Verbindungen verwaltet,dispatcher, und für einen Proxy übergeben Sie einenProxyAgent.
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:
npm install undiciImportieren Sie dann fetch und ProxyAgent aus demselben Paket:
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:
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ötigDie 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:
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" node app.mjsIn der Windows PowerShell:
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjsDiese 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:
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:
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:
npm install axios https-proxy-agentimport 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:
npm install node-fetch https-proxy-agentimport 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:
npm install socks-proxy-agentimport 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?
| Client | Wie wird der Proxy übergeben? | Umgebungsvariable | SOCKS5 | Authentifizierungsfehler |
|---|---|---|---|---|
| Integriertes fetch | NODE_USE_ENV_PROXY oder --use-env-proxy | Nur mit dem Schalter | Keine | fetch failed |
undici fetch | dispatcher: new ProxyAgent(...) | Mit EnvHttpProxyAgent | Keine | fetch failed, Ursache: Anfrage abgebrochen |
| Axios | Option proxy oder httpsAgent + proxy: false | Wenn keine Option gesetzt ist | Mit socks-proxy-agent | Fehler mit Statuscode 407 |
| node-fetch | agent | Keine | Mit socks-proxy-agent | Hängt vom Agent ab |
http.request | agent | Keine | Mit socks-proxy-agent | Hä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.
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:
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.allSettledverhindert, dass eine fehlgeschlagene Anfrage im Stapel die anderen stoppt. MitPromise.allwürde ein einzelnes503die Ergebnisse des ganzen Stapels kosten. Im Beispiel wurde die Adresse/status/503nach vier Versuchen alsrejectedmarkiert, 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:
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 istdispatcher. - Den
ProxyAgentaus npm-undici an das globale fetch übergeben. Passen die Versionen nicht, erhalten Siefetch failed; importieren Siefetchebenfalls aus undici. HTTPS_PROXYsetzen und davon ausgehen, dass integriertes fetch es liest. OhneNODE_USE_ENV_PROXYoder--use-env-proxyliest es sie nicht.- In Axios bei einem Agent
proxy: falsenicht 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 Siesocks5h://. - Kein Timeout setzen. Ein Proxy-Ausgang, der nicht antwortet, lässt eine Anfrage ohne Timeout minutenlang warten. Nutzen Sie in undici
AbortSignal.timeoutund in Axiostimeout. - Alle Anfragen auf einmal mit
Promise.allstarten. Das belastet Ihren eigenen Verbindungspool ebenso wie das Rate-Limit der Zielseite.
Welche Methode sollten Sie wählen?
| Ihre Situation | Empfehlung |
|---|---|
| Neues Projekt, wenige Abhängigkeiten | undici fetch + ProxyAgent |
| Bestehendes Skript ohne Codeänderung über einen Proxy leiten | NODE_USE_ENV_PROXY=1 + HTTPS_PROXY |
| Das Projekt nutzt bereits Axios | Axios + https-proxy-agent + proxy: false |
| Älteres Projekt mit node-fetch | node-fetch + agent |
| SOCKS5-Proxy | Axios oder node-fetch + socks-proxy-agent (socks5h://) |
| Bei jeder Anfrage eine andere IP | Rotating-Proxy, eine Adresse |
| Dieselbe IP für die Sitzung | Sticky-Proxy |
| Klare Fehlermeldungen sind wichtig | Axios (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.




