# How to Use a Proxy in Puppeteer

> Use a proxy in Puppeteer: set it via launch args, handle authenticated proxies with page.authenticate, rotate per page with contexts, and avoid the common mistakes.

[Home](https://quanticdata.io/)/[Blog](https://quanticdata.io/blog/)/How to Use a Proxy in Puppeteer

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

GuidesJul 30, 2026·5 min read·QuanticData Team

On this page [The basic setup: a launch argument](/blog/how-to-use-a-proxy-in-puppeteer/#the-basic-setup-a-launch-argument) [Authenticated proxies](/blog/how-to-use-a-proxy-in-puppeteer/#authenticated-proxies) [Testing the proxy before the real run](/blog/how-to-use-a-proxy-in-puppeteer/#testing-the-proxy-before-the-real-run) [Rotating proxies per page](/blog/how-to-use-a-proxy-in-puppeteer/#rotating-proxies-per-page) [Proxy plus stealth: the real-world combination](/blog/how-to-use-a-proxy-in-puppeteer/#proxy-plus-stealth-the-real-world-combination) [Common mistakes](/blog/how-to-use-a-proxy-in-puppeteer/#common-mistakes) [When Puppeteer plus proxy is more than you need](/blog/how-to-use-a-proxy-in-puppeteer/#when-puppeteer-plus-proxy-is-more-than-you-need)

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](https://quanticdata.io/rotating-proxies/), 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](https://quanticdata.io/residential-proxies/) 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](https://quanticdata.io/blog/playwright-stealth-in-python/), and it applies identically to Puppeteer.

## Common mistakes

| Symptom | Cause | Fix |
| --- | --- | --- |
| Blank page / 407 with paid proxy | Credentials in the `--proxy-server` URL | Use `page.authenticate()`, not `user:pass@` |
| Proxy ignored | Flag on the page or set after launch | It must be a launch arg on the browser |
| Still blocked despite proxy | Headless automation tells | Add the stealth plugin + residential IPs |
| All pages share one IP | Proxy is browser-wide by design | Use 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](https://quanticdata.io/web-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](https://quanticdata.io/blog/how-to-use-playwright-for-scraping/), and it holds for Puppeteer just the same.

### Sources & further reading

- [Stack Overflow — How to use proxy in Puppeteer and headless Chrome](https://stackoverflow.com/questions/48291063/how-to-set-a-proxy-for-puppeteer-headless-chrome)

- [Webshare — Proxy in Puppeteer: setup methods explained](https://www.webshare.io/academy-article/puppeteer-proxy)

## FAQ

Quick answers on how to use proxy in puppeteer.

[Something else? Ask us →](mailto:hello@quanticdata.io)

### 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.

[Start free — $2/month included](https://app.quanticdata.io/register)[Explore Web Scraping API](https://quanticdata.io/web-scraping-api/)

## Related reading

[Guides Playwright Stealth in Python Install playwright-stealth, understand which automation tells it patches, why sophisticated detectors still win, and the IP-plus-fingerprint combination that lasts. Read →](https://quanticdata.io/blog/playwright-stealth-in-python/) [Guides How to Use a Proxy with Python Requests The proxies dict, authenticated and SOCKS5 proxies, session reuse, rotating per request, and the mistakes — HTTPS key, verify, env vars — that silently break it. Read →](https://quanticdata.io/blog/how-to-use-a-proxy-with-python-requests/) [Guides How to Use MCP in Cursor What MCP gives Cursor's agent, how to add a server in mcp.json, project vs global scope, approving tool calls, and connecting a web-data server for live scraping. Read →](https://quanticdata.io/blog/how-to-use-mcp-in-cursor/)

---

Source: https://quanticdata.io/blog/how-to-use-a-proxy-in-puppeteer/ · Site index for AI: https://quanticdata.io/llms.txt
