Documentation Blog Free tools [email protected]Log in

How to use a proxy in Puppeteer: launch args, authentication and rotation

Puppeteer launches headless Chrome with a proxy-server argument, routing pages through a proxy to the targetPuppeteer--proxy-server=host:7777headless Chrome, at launchpage.authenticate() for credsProxy endpointrotating exit IPTargetsees the proxy IP

To use a proxy in Puppeteer, pass Chrome a --proxy-server launch argument when you start the browser. That routes every page through the proxy. Authenticated proxies need one extra step — page.authenticate() — and rotating per page means using separate browser contexts. Those three pieces cover almost every real setup.

The basic setup: a launch argument

Puppeteer controls headless Chrome, and Chrome takes the proxy as a command-line flag. Pass it in the args array at launch:

const browser = await puppeteer.launch({
  headless: true,
  args: ["--proxy-server=http://pr.quanticdata.io:7777"],
});
const page = await browser.newPage();
await page.goto("https://ipinfo.io/ip");
console.log(await page.evaluate(() => document.body.innerText));

Every request from this browser now exits through the proxy. Note the proxy is set on the browser, not the page — all pages in this browser share it. That is fine for a single-IP session; for per-page rotation, see below.

Authenticated proxies

Here is the gotcha that fills Stack Overflow threads: Chrome's --proxy-server flag does not accept user:pass@ in the URL. Credentials must be supplied separately, per page, with page.authenticate() before you navigate:

const browser = await puppeteer.launch({
  args: ["--proxy-server=http://pr.quanticdata.io:7777"],
});
const page = await browser.newPage();
await page.authenticate({ username: "USER", password: "PASS" });
await page.goto("https://example.com");

Forgetting page.authenticate() is the number-one reason a paid proxy "doesn't work" in Puppeteer — you get a blank page or a 407 because the proxy rejected the unauthenticated request. Call it on every page that uses the proxy, before goto.

Testing the proxy before the real run

Always confirm the proxy is actually routing before you point Puppeteer at your real target — a misconfigured proxy that silently falls back to your server's IP is how a scraper gets the whole box banned on the first run. Navigate to an IP-echo endpoint and read the result: it should show the proxy's exit IP, not your machine's. On a rotating endpoint, load it twice and you should see two different IPs, confirming rotation is live. This ten-line check runs in seconds and saves the class of bug where everything looks fine locally but every request has been hitting the target from one bare, un-proxied address.

Rotating proxies per page

Because the proxy is set at launch, rotating a different IP per page means either relaunching the browser (heavy) or using separate browser contexts — but Chrome applies the launch proxy to all contexts, so true per-context proxies need a proxy-per-launch or an endpoint that rotates for you. The clean answer is the latter: point the single launch proxy at a rotating endpoint, and the provider hands out a fresh IP per request automatically — no relaunching, no context juggling. That removes the whole problem: one launch arg, and every navigation gets a new exit IP. For sites that need the same IP across a multi-step flow, use a sticky-session endpoint instead.

Proxy plus stealth: the real-world combination

A proxy fixes the IP, but headless Chrome still leaks automation signals that detectors read. For defended targets, pair the proxy with puppeteer-extra-plugin-stealth to patch those tells, and use residential IPs so the exit looks like a real user rather than a datacenter range. The combination — residential proxy + stealth + human-like pacing — is what actually survives serious anti-bot systems; the proxy alone or stealth alone gets flagged. It's the same recipe we detail for Playwright stealth, and it applies identically to Puppeteer.

Common mistakes

SymptomCauseFix
Blank page / 407 with paid proxyCredentials in the --proxy-server URLUse page.authenticate(), not user:pass@
Proxy ignoredFlag on the page or set after launchIt must be a launch arg on the browser
Still blocked despite proxyHeadless automation tellsAdd the stealth plugin + residential IPs
All pages share one IPProxy is browser-wide by designUse a rotating endpoint for per-request IPs

When Puppeteer plus proxy is more than you need

Running Puppeteer means operating a headless browser fleet plus the proxy and stealth layers — real infrastructure that needs maintaining as detectors evolve. If your goal is the data rather than browser control, a scraping API runs the browser, residential proxies and anti-block server-side and returns clean Markdown or JSON: you send a URL, you get data, with no launch args, no page.authenticate(), no stealth plugin to keep current. Keep Puppeteer for the interactive and logged-in flows where you need to drive the browser directly; hand the high-volume, gets-blocked scraping to an API built to win that fight. We walk through the tradeoff in our Playwright scraping guide, and it holds for Puppeteer just the same.

Sources & further reading

FAQ

Quick answers on how to use proxy in puppeteer.

Something else? Ask us →

How do I use an authenticated proxy in Puppeteer?

Set the proxy host in the --proxy-server launch argument (without credentials), then call page.authenticate({ username, password }) on each page before navigating. Chrome's --proxy-server flag does not accept user:pass@ in the URL, so forgetting page.authenticate() is the top reason a paid proxy returns a blank page or 407.

Why isn't my Puppeteer proxy working?

Usually one of three things: you put credentials in the --proxy-server URL (use page.authenticate instead), you set the proxy on the page rather than as a browser launch argument, or the headless automation is being detected despite the proxy. Check authentication first, then confirm the flag is in the launch args array.

How do I rotate proxies in Puppeteer?

Because the proxy is a browser-wide launch argument, the clean way to rotate is to point that single launch proxy at a rotating endpoint — the provider assigns a fresh IP per request automatically, with no relaunching or context juggling. For a fixed IP across a multi-step flow, use a sticky-session endpoint instead.

Do I need stealth as well as a proxy in Puppeteer?

For defended targets, yes. A proxy fixes the IP, but headless Chrome still leaks automation signals detectors read. Pair the proxy with puppeteer-extra-plugin-stealth and use residential IPs plus human-like pacing. The proxy alone or stealth alone gets flagged; the combination is what survives serious anti-bot systems.

Puppeteer with a proxy vs a scraping API — which should I use?

Use Puppeteer plus a proxy for interactive or logged-in flows where you need to drive the browser directly. Use a scraping API for high-volume scraping that keeps getting blocked — it runs the browser, residential proxies and anti-block server-side and returns clean data, so you stop maintaining a fleet and an evasion arms race.

Skip the fleet, keep the data

Hand high-volume scraping to an API that runs headless Chrome, residential proxies and anti-block for you — send a URL, get clean Markdown or JSON. Pay per success, $2 free every month.

Related reading