---
title: "Puppeteer mit Proxy: Authentifizierung, Rotation und SOCKS5"
description: "Puppeteer nutzt einen Proxy über Chromes Flag --proxy-server oder pro Browser-Context, angemeldet mit page.authenticate(). Getestete Beispiele und Fehler."
url: https://proxynet.io/de/blog/puppeteer-proxy
date: 2026-10-06
author: "Acar Diveroli"
category: "Integration, Web Scraping"
lang: de
---

# Puppeteer mit Proxy: Authentifizierung, Rotation und SOCKS5

Sie schreiben `--proxy-server=http://user:pass@host:port` in die Startargumente, so wie Sie einen Proxy für cURL angeben würden, und der erste `page.goto()` bricht mit `net::ERR_NO_SUPPORTED_PROXIES` ab. Sie nehmen die Zugangsdaten heraus, und der Fehler lautet nun `net::ERR_INVALID_AUTH_CREDENTIALS`. Puppeteer hat keine eigene Proxy-Option. Es startet Chrome, und Chrome entscheidet, wie ein Proxy verwendet wird; die meisten Antworten stecken deshalb in den Regeln von Chrome und nicht in der API von Puppeteer.

Dieser Beitrag behandelt das Start-Flag und `page.authenticate()`, einen Proxy pro Browser-Context, Sticky- und Rotating-Sitzungen, SOCKS5, die Bypass-Liste, die Prüfung der Ausgangs-IP, die typischen Fehler und ein vollständiges Skript mit Wiederholungen. Jedes Beispiel lief am 6. Oktober 2026 mit Puppeteer 25.12.0 und dem Chrome-154-Build, den es herunterlädt, gegen einen lokalen HTTP-Proxy mit Benutzername und Passwort sowie gegen einen lokalen SOCKS5-Server. Die zitierten Fehlermeldungen sind genau die, die wir erhalten haben.

> **Hinweis: Kurzantwort**
>
> Puppeteer übergibt den Proxy an Chrome: Schreiben Sie `--proxy-server=http://host:port` in die `args` von `puppeteer.launch()`, oder übergeben Sie `proxyServer` an `browser.createBrowserContext()`, um einem einzelnen Context einen eigenen Proxy zu geben. Benutzername und Passwort gehören nie in diese Adresse; rufen Sie `page.authenticate({ username, password })` vor dem ersten `page.goto()` auf. Chrome verwendet diese Zugangsdaten danach für den gesamten Context weiter, eine Proxy-Sitzung bedeutet also einen Context. SOCKS5 funktioniert nur ohne Passwort, deshalb wird es mit einer IP-Whitelist kombiniert.

## Wie leitet Puppeteer den Datenverkehr über einen Proxy?

Puppeteer ist eine Node.js-Bibliothek, die einen echten Browser aus dem Code heraus steuert. Sie öffnet selbst keine Netzwerkverbindungen; das tut der Browser. Ein Proxy ist deshalb eine Chrome-Einstellung, die entweder als Kommandozeilen-Flag beim Browserstart oder als Option beim Anlegen eines Browser-Contexts übergeben wird. Ein Browser-Context ist eine isolierte Sitzung innerhalb eines Browsers mit eigenen Cookies, eigenem Cache und eigenem Speicher, ähnlich einem Inkognitofenster.

Die Anmeldung am Proxy ist ein eigener Schritt, weil Chrome keine Zugangsdaten aus der Proxy-Adresse liest. Unser Test-Proxy hat für eine HTTPS-Seite diesen Ablauf protokolliert:

1. Chrome sendet `CONNECT httpbin.org:443` und bittet den Proxy damit, einen Tunnel zur Website zu öffnen. Bei einer `http://`-Seite sendet es direkt die Anfrage selbst.
2. Die Anfrage enthält keine Zugangsdaten, also antwortet der Proxy mit `407 Proxy Authentication Required`. [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110#status.407) definiert 407 als Gegenstück zu 401 auf der Seite des Proxys.
3. Puppeteer fängt diese Aufforderung ab und antwortet mit dem Benutzernamen und dem Passwort, die `page.authenticate()` übergeben wurden.
4. Chrome wiederholt die Anfrage mit einem `Proxy-Authorization`-Header, der Proxy antwortet mit `200`, und die verschlüsselte Verbindung zur Website wird innerhalb des Tunnels aufgebaut. Der Proxy sieht den Hostnamen, nicht den Seiteninhalt.
5. Chrome legt die Zugangsdaten im Authentifizierungs-Cache des Contexts ab und sendet sie bei späteren Verbindungen sofort mit.

Wegen Schritt 5 gehören die Zugangsdaten am Ende zum Context, wie die Tests weiter unten zeigen.

## Wie wird ein Proxy mit --proxy-server und page.authenticate() gesetzt?

`npm i puppeteer` installiert die Bibliothek und lädt ein passendes Chrome herunter; `puppeteer-core` ist dieselbe API ohne diesen Download. Die minimale Einrichtung:

```js
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.proxynet.io:8000"],
});
const page = await browser.newPage();
await page.authenticate({ username: "user", password: "pass" });

await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText)); // die Ausgangs-IP, die die Website sieht

await browser.close();
```

Das `http://` im Flag beschreibt die Verbindung zum Proxy, nicht die Seiten. Ein HTTP-Proxy transportiert auch `https://`-Websites durch den Tunnel, und ihr Inhalt bleibt zwischen Chrome und der Website verschlüsselt. Die [Referenz zu `page.authenticate()`](https://pptr.dev/api/puppeteer.page.authenticate) ergänzt, dass Puppeteer dafür im Hintergrund das Abfangen von Anfragen (Request Interception) einschaltet, was etwas Leistung kosten kann; mit `null` schalten Sie es wieder ab.

In unseren Tests gingen drei Dinge schief, und aus jedem folgt eine feste Regel:

- **Rufen Sie `page.authenticate()` vor dem ersten `goto()` auf.** Erst danach aufgerufen, scheiterte die erste Navigation mit `net::ERR_INVALID_AUTH_CREDENTIALS`; erst die nächste kam durch.
- **Lassen Sie die Zugangsdaten aus dem Flag heraus.** Mit `user:pass@` in der Adresse lehnte Chrome 154 den gesamten Wert mit `net::ERR_NO_SUPPORTED_PROXIES` ab. Ältere Anleitungen beschreiben an dieser Stelle einen `407`; heute erreicht die Anfrage den Proxy gar nicht.
- **Senden Sie `Proxy-Authorization` nicht selbst.** Als wir den Header mit `page.setExtraHTTPHeaders()` setzten, verweigerte Chrome die Navigation mit `net::ERR_INVALID_ARGUMENT`.

| Wert von `--proxy-server` | Ergebnis in Chrome 154 |
|---|---|
| `http://pr.proxynet.io:8000` | Funktioniert; HTTPS-Seiten laufen durch einen `CONNECT`-Tunnel |
| `pr.proxynet.io:8000` | Funktioniert; ohne Schema nimmt Chrome einen HTTP-Proxy an |
| `http://user:pass@pr.proxynet.io:8000` | `net::ERR_NO_SUPPORTED_PROXIES` |
| `socks5://host:port` | Funktioniert nur, wenn der Server kein Passwort verlangt |
| `socks5h://host:port` | `net::ERR_NO_SUPPORTED_PROXIES`; Chrome kennt dieses Schema nicht |

## Wie bekommt jeder Browser-Context einen eigenen Proxy?

Puppeteer 22 hat `createIncognitoBrowserContext()`, den Namen aus vielen älteren Anleitungen, in `browser.createBrowserContext()` umbenannt. Dessen [`BrowserContextOptions`](https://pptr.dev/api/puppeteer.browsercontextoptions) enthalten zwei Proxy-Felder, `proxyServer` und `proxyBypassList`. Der Browser kann ohne Proxy starten, während jeder Context seinen eigenen bekommt:

```js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
// Eine Sticky-Sitzung pro Context: gleiche Sitzungs-ID -> gleiche Ausgangs-IP für bis zu 600 Sekunden
const sessions = ["a1b2c3", "d4e5f6"];

const browser = await puppeteer.launch(); // der Browser selbst startet ohne Proxy
for (const id of sessions) {
  const context = await browser.createBrowserContext({ proxyServer: PROXY });
  const page = await context.newPage();
  await page.authenticate({ username: `user-session-${id}-ttl-600`, password: "pass" });

  for (let i = 0; i < 2; i++) {
    await page.goto("https://httpbin.org/ip");
    console.log(id, await page.$eval("body", (el) => el.innerText));
  }
  await context.close(); // Cookies, Cache und Proxy-Einstellung verschwinden mit dem Context
}
await browser.close();
```

Unser Test-Proxy vergibt die Ausgänge nach Sitzungs-ID, so wie es ein Gateway tut. Die beiden Contexts gingen über zwei verschiedene Adressen hinaus, und jeder behielt seine Adresse bei der zweiten Anfrage. Zwei weitere Ergebnisse sind für echte Aufgaben wichtig:

- **Zugangsdaten gehören zum Context, nicht zur Seite.** Eine zweite Seite im selben Context rief `page.authenticate()` mit einem anderen Benutzernamen auf, und trotzdem trug jede ihrer Anfragen weiterhin den ersten Benutzernamen. Eine Seite, die die Methode nie aufgerufen hatte, kam mit denselben zwischengespeicherten Zugangsdaten durch.
- **Ein Context ohne `proxyServer` erbt den Proxy des Start-Flags, behält aber seine eigenen Zugangsdaten.** Mit `--proxy-server` am Browser gingen zwei Contexts mit eigenen Sitzungs-IDs trotzdem über zwei verschiedene Ausgänge hinaus.

| | Flag `--proxy-server` | `createBrowserContext({ proxyServer })` |
|---|---|---|
| Geltungsbereich | Der gesamte Browser | Nur dieser Context |
| Getrennte Ausgänge in einem Browser | Ja, ein Benutzername pro Context | Ja, sogar getrennte Proxy-Hosts |
| Proxy wechseln | Browser neu starten | Context schließen, einen neuen öffnen |
| Geeignet für | Ein Skript, ein Ausgang | Viele unabhängige Sitzungen parallel |

Die Arbeitsregel lautet: eine Sitzung, ein Context, ein Benutzername.

## Rotating oder Sticky: Welcher Sitzungstyp passt zu einem Browser?

Ein Browser sendet nicht eine Anfrage pro Seite. Eine Produktseite lädt das HTML-Dokument, Skripte, Stylesheets, Bilder und API-Aufrufe im Hintergrund, oft von mehreren Hosts über mehrere Verbindungen. An einem rotierenden Gateway kann jede neue Verbindung eine neue Ausgangs-IP bekommen; in unserem Test ging sogar das Favicon einer Seite über eine andere Adresse hinaus als die Seite selbst. Für voneinander unabhängige Seiten ist das harmlos. Bei einem Login, einem Warenkorb oder einem mehrstufigen Formular sieht die Website dagegen einen Besucher, der zwischen Adressen springt, und beendet die Sitzung womöglich.

Das Gateway liest den Sitzungstyp aus dem Benutzernamen, den der Endpoint-Generator im Dashboard für Sie zusammensetzt. Seine Teile sind leicht zu lesen: `-country-de` wählt das Ausgangsland (die Stadt wählen Sie im selben Generator), `-session-a1b2c3-ttl-600` hält für diese Sitzungs-ID einen Ausgang für die angegebene Zahl an Sekunden, irgendwo zwischen 1 und 60 Minuten, und ein Benutzername ohne Sitzungsteil rotiert.

Für unabhängige Seiten wie Kategorielisten oder Suchergebnisse, die in kurzlebigen Contexts geöffnet werden, eignet sich ein [Rotierender Proxy](https://proxynet.io/de/rotating-proxy).

Für alles, was einen Zustand hält, eignet sich ein [Sticky-Proxy](https://proxynet.io/de/sticky-proxy): ein Context, eine Sitzungs-ID, länger gehalten, als der Ablauf dauert. Wenn die Aufgabe endet, schließen Sie den Context, damit Cookies und IP gemeinsam enden.

Rotation verteilt unabhängige Arbeit auf verschiedene Ausgänge; sie ist kein Weg um die Limits einer Website herum. Ein `429 Too Many Requests` oder `403 Forbidden` fordert Sie auf, langsamer zu werden, und eine neue IP ändert an dieser Antwort nichts.

## Funktioniert Puppeteer mit einem SOCKS5-Proxy?

Ja, aber ohne Benutzername und Passwort. [Die Proxy-Dokumentation von Chromium](https://chromium.googlesource.com/chromium/src/+/HEAD/net/docs/proxy.md) hält fest, dass für SOCKSv5 kein Authentifizierungsverfahren unterstützt wird. Unser lokaler SOCKS5-Server hat das von der anderen Seite gesehen: Die Begrüßung von Chrome bot nur die Methode `0x00` an, „keine Authentifizierung“. Ein Server, der die Methode mit Benutzername und Passwort (`0x02`) verlangte, musste dieses Angebot ablehnen, und die Navigation scheiterte mit `net::ERR_SOCKS_CONNECTION_FAILED`. `page.authenticate()` änderte daran nichts, weil es bei SOCKS5 keine Aufforderung nach Art von 407 gibt, auf die Puppeteer antworten könnte.

Die Lösung besteht darin, sich über die IP-Adresse auszuweisen. Tragen Sie die öffentliche IP des Rechners, auf dem Chrome läuft, im Dashboard in die IP-Whitelist ein (bis zu 10 Adressen), nehmen Sie den SOCKS5-Port, den der Endpoint-Generator anzeigt, und starten Sie ohne Zugangsdaten:

```js
import puppeteer from "puppeteer";

// Kein Benutzername, kein Passwort: Die öffentliche IP dieses Rechners muss auf der IP-Whitelist stehen.
// SOCKS5_PORT: der SOCKS5-Port, den der Endpoint-Generator anzeigt.
const { SOCKS5_PORT } = process.env;

const browser = await puppeteer.launch({
  args: [`--proxy-server=socks5://pr.proxynet.io:${SOCKS5_PORT}`],
});
const page = await browser.newPage();
await page.goto("https://httpbin.org/ip");
console.log(await page.$eval("body", (el) => el.innerText));
await browser.close();
```

Chrome schickte dem SOCKS5-Server Hostnamen, keine IP-Adressen. DNS wird also am Proxy aufgelöst, und die von cURL bekannte Gewohnheit `socks5h://` ist hier nicht nötig. Für Webseiten ist ein HTTP-Proxy meist die einfachere Wahl, weil er mit `page.authenticate()` funktioniert.

## Wie werden bestimmte Adressen vom Proxy ausgenommen?

Das Flag `--proxy-bypass-list` oder `proxyBypassList` als Array an einem Context listet Hosts auf, die Chrome direkt erreicht. Im Flag werden die Regeln durch Semikolons oder Kommas getrennt: `--proxy-bypass-list=*.internal.example;192.168.0.0/16`. In unserem Test umging ein gelisteter Host den Proxy vollständig und wurde auf dem lokalen Rechner aufgelöst.

Chrome hat außerdem implizite Regeln: `localhost`, `*.localhost`, `127.0.0.1/8` und `[::1]` nutzen nie den Proxy, auch bei leerer Liste. Wenn Sie gegen einen lokalen Server testen und das Proxy-Log stumm bleibt, liegt es daran. Die Sonderregel `<-loopback>` hebt diese impliziten Regeln auf; damit lief unsere Anfrage an `127.0.0.1` über den Proxy.

## Wie wird die Ausgangs-IP geprüft?

Vergleichen Sie im selben Browser einen direkten Context mit einem Context, der über den Proxy läuft. Wenn sich die Adressen unterscheiden, geht der Datenverkehr über den Proxy:

```js
import puppeteer from "puppeteer";

async function exitIp(context, credentials) {
  const page = await context.newPage();
  if (credentials) await page.authenticate(credentials);
  await page.goto("https://httpbin.org/ip");
  const { origin } = JSON.parse(await page.$eval("body", (el) => el.innerText));
  await page.close();
  return origin;
}

const browser = await puppeteer.launch();
const direct = await browser.createBrowserContext();
const proxied = await browser.createBrowserContext({ proxyServer: "http://pr.proxynet.io:8000" });

const own = await exitIp(direct);
const viaProxy = await exitIp(proxied, { username: "user", password: "pass" });
console.log({ own, viaProxy, proxyWorks: own !== viaProxy });
await browser.close();
```

Wenn Sie ein bestimmtes Land ansteuern, schlagen Sie die Ausgangs-IP zusätzlich in einem Geolocation-Dienst nach. Schlägt die Prüfung fehl, nehmen Sie Chrome aus der Gleichung: [Funktioniert mein Proxy? So testen Sie einen Proxy](/de/blog/how-to-test-a-proxy) enthält die Kommandozeilentests, die ein Netzwerkproblem von einem Codeproblem unterscheiden.

## Welche Fehler treten auf, und was bedeuten sie?

Jeden dieser Fehler haben wir im lokalen Testaufbau selbst erzeugt:

| Was Sie sehen | Was passiert ist | Was zu tun ist |
|---|---|---|
| `net::ERR_PROXY_CONNECTION_FAILED` | Chrome konnte den Proxy nicht erreichen: falscher Host oder Port, oder ausgehender Datenverkehr wird blockiert | Adresse und Port prüfen; Wiederholen hilft nicht |
| `net::ERR_INVALID_AUTH_CREDENTIALS` | Der Proxy verlangte eine Anmeldung, und Chrome hatte keine Zugangsdaten | `page.authenticate()` vor dem ersten `goto()` aufrufen |
| `goto()` liefert Status `407` | Das Passwort war falsch; Puppeteer versuchte es einmal und gab auf | `response.status()` prüfen, Zugangsdaten korrigieren |
| `net::ERR_TUNNEL_CONNECTION_FAILED` | Die Anmeldung klappte, aber der Proxy konnte den Tunnel zur Website nicht öffnen | Zieladresse prüfen |
| `net::ERR_NO_SUPPORTED_PROXIES` | Chrome lehnte den Wert von `--proxy-server` ab | `user:pass@` entfernen, `http://` oder `socks5://` verwenden |
| `net::ERR_SOCKS_CONNECTION_FAILED` | Der SOCKS5-Server will ein Passwort, oder Ihre IP steht nicht auf der Whitelist | IP auf die Whitelist setzen oder den HTTP-Port nutzen |
| Seite lädt, aber die IP ist Ihre eigene | Eine Bypass-Regel oder ein Loopback-Host hat den Proxy umgangen | Bypass-Liste und `<-loopback>` prüfen |

Die Zeile mit dem falschen Passwort bleibt am längsten unentdeckt: Nichts wirft einen Fehler, und das Skript arbeitet mit der 407-Seite des Proxys weiter. In einem Desktop-Browser hat der Tunnelfehler mehr Ursachen, vom HTTPS-Scan eines Virenschutzprogramms bis zu einem veralteten System-Proxy; dazu mehr in [err_tunnel_connection_failed in Chrome und Edge beheben](/de/blog/err-tunnel-connection-failed). Während der Entwicklung zeigen zwei Listener den echten Fehler hinter einem Timeout:

```js
page.on("requestfailed", (req) => console.log("FEHLGESCHLAGEN", req.url(), req.failure()?.errorText));
page.on("response", (res) => res.status() >= 400 && console.log(res.status(), res.url()));
```

## Vollständiges Beispiel: parallele Contexts, Sticky-Sitzungen und Wiederholungen

Das Skript öffnet sechs per JavaScript gerenderte Seiten mit höchstens drei Contexts gleichzeitig. Jeder Versuch bekommt einen frischen Context und eine frische Sticky-Sitzung, sodass Cookies und Ausgangs-IP gemeinsam wechseln. Bilder, Medien und Fonts werden blockiert, um Datenverkehr zu sparen; `page.setRequestInterception()` erledigt das, ohne `page.authenticate()` zu stören. Timeouts und Tunnelfehler werden mit exponentiellem Backoff wiederholt, während Konfigurationsfehler und ein `403` oder `429` der Website die Aufgabe beenden.

```js
import puppeteer from "puppeteer";

const PROXY = "http://pr.proxynet.io:8000";
const USER = "user";
const PASS = "pass";
const URLS = Array.from({ length: 6 }, (_, i) => `https://quotes.toscrape.com/js/page/${i + 1}/`);
const CONCURRENCY = 3; // gleichzeitig offene Contexts
const ATTEMPTS = 3;
const BLOCKED = new Set(["image", "media", "font"]);
// Konfigurationsfehler: Warten und Wiederholen beheben sie nicht
const FATAL = ["ERR_PROXY_CONNECTION_FAILED", "ERR_NO_SUPPORTED_PROXIES", "ERR_INVALID_AUTH_CREDENTIALS"];

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function scrape(browser, url) {
  for (let attempt = 1; attempt <= ATTEMPTS; attempt++) {
    // Ein Context pro Versuch: eigene Cookies und eigene Sticky-Sitzung (eine Ausgangs-IP)
    const context = await browser.createBrowserContext({ proxyServer: PROXY });
    try {
      const page = await context.newPage();
      const session = crypto.randomUUID().slice(0, 8);
      await page.authenticate({ username: `${USER}-session-${session}-ttl-300`, password: PASS });
      await page.setRequestInterception(true);
      page.on("request", (req) => (BLOCKED.has(req.resourceType()) ? req.abort() : req.continue()));

      const res = await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30_000 });
      const status = res ? res.status() : 0;
      if (status === 403 || status === 429) {
        // Die Website lehnt ab oder bremst Sie: Eine neue IP ist nicht die Antwort
        throw Object.assign(new Error(`HTTP ${status}: anhalten und die Anfragerate senken`), { fatal: true });
      }
      if (status >= 400) throw new Error(`HTTP ${status}`);

      await page.waitForSelector("div.quote span.text", { timeout: 10_000 }); // per JavaScript gerendert
      return await page.$$eval("div.quote span.text", (els) => els.map((el) => el.textContent));
    } catch (err) {
      err.fatal ||= FATAL.some((code) => err.message.includes(code));
      if (err.fatal || attempt === ATTEMPTS) throw err;
      await sleep(2 ** attempt * 1000 + Math.random() * 1000); // vor dem nächsten Versuch warten
    } finally {
      await context.close();
    }
  }
}

const browser = await puppeteer.launch();
const queue = [...URLS];
const results = new Map();
await Promise.all(
  Array.from({ length: CONCURRENCY }, async () => {
    while (queue.length) {
      const url = queue.shift();
      try {
        results.set(url, `${(await scrape(browser, url)).length} Zitate`);
      } catch (err) {
        results.set(url, `FEHLER ${err.message.split("\n")[0]}`);
        if (err.fatal) queue.length = 0; // die ganze Aufgabe stoppen, nicht nur diese Seite
      }
    }
  }),
);
await browser.close();
for (const url of URLS) console.log(url, results.get(url) ?? "übersprungen");
```

Über den lokalen Test-Proxy kamen alle sechs Seiten in weniger als fünf Sekunden mit je zehn Zitaten zurück. Mit absichtlich falsch eingetipptem Proxy-Port stoppten die drei laufenden Seiten sofort mit `net::ERR_PROXY_CONNECTION_FAILED`, und die anderen drei wurden übersprungen; eine lokale Testseite, die mit `429` antwortete, beendete die Aufgabe auf dieselbe Weise. Beginnen Sie mit einem niedrigen Wert für `CONCURRENCY`: Jeder Context belegt Speicher, und auch die Zielseite hat Grenzen.

## Einsatzbereiche

- Per JavaScript gerenderte Katalog- und Preisseiten, die ein einfacher HTTP-Client leer sieht.
- Prüfen, wie Ihre eigene Website, Ihre Preise oder Ihre Consent-Banner aus einem anderen Land aussehen, mit einem Context pro Land.
- Screenshots und PDFs öffentlicher Seiten, so wie ein Besucher in einem bestimmten Land sie sieht.
- Überwachung Ihrer eigenen Seiten und Checkout-Abläufe von außerhalb Ihres Netzwerks.

Für alle gilt derselbe Rahmen: Halten Sie sich an die `robots.txt` und die Nutzungsbedingungen der Website, nutzen Sie eine offizielle API, wo es eine gibt, und halten Sie die Anfragerate auf einem Niveau, das die Website tragen kann.

## Häufige Fehler

- **`user:pass@` in `--proxy-server` schreiben.** Chrome lehnt den Wert mit `net::ERR_NO_SUPPORTED_PROXIES` ab.
- **Einen neuen Benutzernamen pro Seite erwarten.** Chrome behält die ersten Zugangsdaten für den gesamten Context; nutzen Sie einen Context pro Sitzung.
- **Ein Passwort für SOCKS5 versuchen.** Chrome bietet nur „keine Authentifizierung“ an; nutzen Sie eine IP-Whitelist oder den HTTP-Port.
- **Gegen `localhost` testen und dem Ergebnis trauen.** Loopback-Adressen umgehen den Proxy, solange Sie nicht `<-loopback>` hinzufügen.
- **Eine `407`-Antwort wie eine normale Seite behandeln.** Ein falsches Passwort wirft keinen Fehler; prüfen Sie `response.status()`.
- **Nach einem `429` oder `403` die IP rotieren.** Werden Sie stattdessen langsamer; das Limit betrifft Ihr Verhalten, nicht Ihre Adresse.
- **Contexts offen lassen.** Jeder belegt Speicher; schließen Sie sie in einem `finally`-Block.

## Entscheidungshilfe

| Bedarf | Empfehlung |
|---|---|
| Ein Skript, ein Ausgang | Flag `--proxy-server` plus `page.authenticate()` |
| Viele unabhängige Sitzungen in einem Browser | `createBrowserContext({ proxyServer })`, eine Sitzungs-ID pro Context |
| Viele Seiten, die nicht voneinander abhängen | Rotierender Benutzername, kurzlebige Contexts |
| Login, Warenkorb oder mehrstufiges Formular | Sticky-Sitzung, die länger hält als der Ablauf, ein Context |
| SOCKS5 ist Pflicht | IP-Whitelist, keine Zugangsdaten |
| Ein Werkzeug, das nur das Start-Flag annimmt | IP-Whitelist oder ein lokaler Forwarder wie `proxy-chain` |
| Interne Hosts müssen direkt erreichbar bleiben | `--proxy-bypass-list` oder `proxyBypassList` |

## Häufige Fragen

### Hat puppeteer.launch() eine Proxy-Option?

Nein. Der Proxy kommt als Chrome-Flag `--proxy-server` in `args`, die Anmeldung in `page.authenticate()`. Die eigene API von Puppeteer hat Proxy-Felder nur bei `browser.createBrowserContext()`: `proxyServer` und `proxyBypassList`.

### Kann jede Seite ihren eigenen Proxy haben?

Nicht innerhalb eines Contexts. Proxy und zwischengespeicherte Zugangsdaten gehören zum Context, deshalb nutzte in unserem Test eine zweite Seite mit anderen Zugangsdaten trotzdem die ersten. Geben Sie jeder Seite, die einen eigenen Ausgang braucht, einen eigenen Context; ein Context ist weit günstiger als ein neuer Browser.

### Was ist proxy-chain, und brauchen Sie es?

`proxy-chain` ist ein Open-Source-Paket für Node.js, dessen Funktion `anonymizeProxy()` einen lokalen Proxy ohne Passwort startet und den Datenverkehr an einen Upstream-Proxy mit Zugangsdaten weiterleitet, sodass Chrome eine einfache Adresse `http://127.0.0.1:<port>` bekommt. In unserem Test funktionierte das sowohl mit einem HTTP- als auch mit einem SOCKS5-Upstream, der ein Passwort verlangte. Mit `page.authenticate()` oder einer IP-Whitelist brauchen Sie es nicht.

### Lässt sich der Proxy ohne Neustart des Browsers wechseln?

Ja, über Contexts. Schließen Sie den Context, öffnen Sie einen neuen mit einem anderen `proxyServer` oder einer anderen Sitzungs-ID, und rufen Sie `page.authenticate()` erneut auf. Nur das Flag `--proxy-server` ist für die gesamte Lebensdauer des Browsers festgelegt.

### Warum zeigt das Proxy-Log Anfragen, die Sie nicht gestellt haben?

Der eigene Hintergrund-Traffic von Chrome, etwa Update-Prüfungen und Anfragen an Google-Dienste, geht an denselben Proxy. In unserem Log kam der Großteil davon ohne Zugangsdaten an und erhielt einen `407`, weil `page.authenticate()` nur auf Aufforderungen aus dem Datenverkehr der Seiten antwortet. Ihre Seiten beeinflusst das nicht.

### Verhindert ein Proxy CAPTCHAs in Puppeteer?

Nein. Ein Proxy ändert, woher der Datenverkehr kommt, während CAPTCHAs auch auf die Anfragerate, auf Browsersignale und auf widersprüchliche Sitzungen reagieren. Die Ursachen und die legitimen Wege, sie seltener zu machen, stehen in [Puppeteer und CAPTCHA: warum, und wie seltener?](/de/blog/puppeteer-captcha).

## Fazit

In Puppeteer ist der Proxy eine Chrome-Einstellung: das Flag `--proxy-server` für den gesamten Browser oder `proxyServer` für einen einzelnen Context, mit der Anmeldung über `page.authenticate()` vor der ersten Navigation. Zugangsdaten werden pro Context zwischengespeichert; bauen Sie deshalb jede Sitzung als eigenen Context, wählen Sie Sticky oder Rotating über den Benutzernamen und nutzen Sie für SOCKS5 eine IP-Whitelist. Prüfen Sie `response.status()` und halten Sie die Anfragerate höflich. Für Seiten, die sich über gewöhnliche Heimanschlüsse öffnen sollen, liefert ein [Residential-Proxy](https://proxynet.io/de/residential-proxy) die Ausgänge.
