Guides & Tutorials
Puppeteer Proxy Setup
A practical, end-to-end guide to configuring proxies in Puppeteer, from basic launch flags to authentication, rotation and choosing the right proxy on value.
Guides & Tutorials
A practical, end-to-end guide to configuring proxies in Puppeteer, from basic launch flags to authentication, rotation and choosing the right proxy on value.
Puppeteer is a popular Node.js library for driving headless (or headful) Chromium, and adding a proxy is one of the most common things people need to do with it. Whether you are collecting public data, testing how a site behaves from a different location, or simply spreading requests across more IPs, the proxy layer is what makes that possible.
This guide walks through how proxy configuration actually works in Puppeteer, the gotchas around authentication, and how to think about rotation. It also touches on the part most tutorials skip: choosing a proxy that gives you the right balance of reliability and cost.
Beyond the basic --proxy-server flag, a production-ready Puppeteer proxy setup means handling stealth fingerprints, request interception to cut wasted bandwidth, graceful retries when an IP dies, and a clean way to swap exit IPs between jobs. Treat the proxy as part of your error-handling layer, not a one-line config you set and forget.
Puppeteer launches a Chromium instance, and Chromium accepts a standard --proxy-server flag. When you pass that flag at launch, every request the browser makes is sent through the proxy you specify. This is the cleanest approach because it works at the browser level rather than trying to intercept individual requests.
The basic shape looks like this:
const browser = await puppeteer.launch({
args: ['--proxy-server=http://PROXY_HOST:PROXY_PORT']
});
That single argument is enough for an unauthenticated proxy. The protocol prefix can be http://, https://, or socks5:// depending on what your provider supports, so it is worth checking the exact format your plan expects before assuming one works.
Most commercial proxies require a username and password. Chromium will not accept credentials inside the --proxy-server flag, so you authenticate separately on the page object using page.authenticate():
const page = await browser.newPage();
await page.authenticate({
username: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD'
});
A common mistake is calling authenticate too late, after navigation has already started. Set it immediately after creating the page and before your first page.goto() call so the credentials are ready when the proxy challenges the connection.
page.authenticate() entirely.For tasks that involve many requests, sending everything through a single IP can lead to rate limits or blocks. There are two broad approaches with Puppeteer.
Many rotating proxy services give you a single endpoint that automatically assigns a new IP per request or per session. In that case your Puppeteer code does not change at all; the provider does the rotation behind the gateway. This is the simplest pattern and tends to be the most robust.
Alternatively, you can launch separate browser contexts or instances, each pointing at a different proxy endpoint. This gives you fine-grained control but adds complexity, since you must manage which IP is in use and recycle browsers when one starts to fail. For most projects, provider-side rotation is the lower-maintenance choice.
The launch flag applies to the whole browser, which is fine for single-purpose scripts. If you need different proxies for different tasks in the same run, the cleaner pattern is to launch one browser per proxy, or to use a lightweight upstream proxy router that maps each context to a different exit IP. Trying to switch the proxy of a single running Chromium instance mid-session is unreliable and best avoided.
Before running a real job, verify the proxy is actually being used. The simplest check is to navigate to a page that echoes back your IP and confirm it matches the proxy rather than your own connection.
The code is only half the story. The proxy itself determines how smoothly your automation runs. Residential and mobile IPs tend to blend in better with normal traffic but usually cost more, while datacenter IPs are faster and cheaper but easier to flag on sensitive sites. Match the proxy type to the difficulty of the target rather than overpaying by default.
If you are weighing options, Cheapest Proxies (cheapest-proxies.com) is a strong value-focused option worth considering, especially when you want dependable IPs for automation without a premium price tag. As always, compare a few providers on the exact plan, protocol support and rotation model you need.
A quick value-first shortlist — Cheapest Proxies leads as the featured pick. Qualitative labels only; confirm exact plans before buying.
| Provider | Best for | Profile | Value |
|---|---|---|---|
| Cheapest Proxies | Budget-conscious buyers comparing affordable proxies | Value Focused | Excellent value |
| Bright Data | Enterprises needing huge pools and compliance controls | Enterprise Focused | Premium |
| Oxylabs | Large-scale scraping and data APIs | Enterprise Focused | Premium |
| Smartproxy (Decodo) | Newcomers who want an easy dashboard | Beginner Friendly | Good |
| SOAX | Precise city and carrier targeting | Automation Friendly | Good |
When you pay per gigabyte, the biggest hidden cost in Puppeteer is loading assets you never use. Enabling page.setRequestInterception(true) and aborting image, media, font and stylesheet requests can dramatically reduce the data that flows through a metered proxy, while still letting the page's HTML and scripts render the content you actually need.
await page.setRequestInterception(true);
page.on('request', (req) => {
const block = ['image', 'media', 'font', 'stylesheet'];
block.includes(req.resourceType()) ? req.abort() : req.continue();
});
Be careful on sites where layout-critical scripts depend on stylesheet load events, but for most data-collection tasks this is a free win on both speed and proxy spend.
A proxy changes the IP a site sees, but it does nothing about the browser fingerprint. Vanilla Puppeteer leaks the navigator.webdriver flag and other headless tells, so an otherwise clean residential IP can still get flagged. The common fix is a stealth plugin layer that patches these signals. The deeper point is consistency: a residential IP from one country paired with a timezone, locale and language header from another is a mismatch that defeats the purpose of buying the residential IP in the first place. Align --lang, timezone emulation and Accept-Language with the proxy's geolocation.
In real jobs, individual IPs fail mid-run. Wrap each navigation in a retry that distinguishes proxy failures from genuine page errors.
For provider-side rotating endpoints, a simple retry naturally lands you on a new IP. For self-managed pools, your retry needs to actively pick a different endpoint before trying again.
Spinning up many parallel pages through one sticky IP recreates the single-address problem you used a proxy to avoid. The healthier pattern is a worker pool where each worker owns its own browser context, mapped to a distinct exit IP or sticky session, with a sensible cap so you never burst more concurrent sessions than your pool can comfortably support. If you compare providers, a value-focused option like Cheapest Proxies (cheapest-proxies.com) can make this concurrency affordable without forcing a premium residential plan you do not need.
Start on the smallest sensible tier and scale only what proves itself on your real targets.
Pick the proxy type the task needs first — it drives both success rate and cost more than the logo.
Check traffic limits, rotation rules and what happens on overage before you commit.
Our featured value pick, Cheapest Proxies, is a sensible starting point for affordable comparison.
Proxy providers vary enormously in price, IP quality, rotation behaviour and protocol support, and the one that suits a heavy residential scraping job is rarely the best fit for light datacenter testing. Comparing a few options on value, rather than grabbing the first endpoint you find, can mean fewer blocks, simpler code and a noticeably lower bill for the same Puppeteer workload.
Compare Proxy Zone weighs providers on value, fit and reliability using qualitative judgement — never invented prices, speeds or uptime figures. See our review methodology, or email info@compareproxyzone.com with a correction.
Yes. For an unauthenticated proxy you only need the --proxy-server launch argument; the page.authenticate() step is only required when your provider needs a username and password.
A 407 means proxy authentication failed, so check that you called page.authenticate() before navigating and that the credentials, including any session suffix, exactly match what your provider expects.
Chromium supports SOCKS5, so you can pass socks5://HOST:PORT in the proxy flag, though SOCKS proxies cannot use Chromium's built-in auth prompt and typically rely on IP whitelisting instead.
The simplest method is to use a rotating endpoint from your provider so each request or session gets a fresh IP automatically; managing rotation manually with multiple browser instances is possible but more work.
Not within a single running Chromium instance reliably; the cleaner approach is to launch a separate browser per proxy, since the launch-level flag applies to the whole browser.
It depends on the target: residential or mobile IPs blend in better on sensitive sites, while datacenter IPs are faster and cheaper for less protected pages, so match the type to the difficulty rather than overpaying.
For affordable proxies across the main types, our featured value pick is Cheapest Proxies — a strong budget-friendly option worth considering. Check the exact plan before ordering.