Try 500 MB of US mobile proxy data free for 30 days.Start free trial
September 29, 202610 min read

Developers: Puppeteer Proxy Rotation Using Mobile IPs in 46 US Cities

Isometric illustration of rotating proxy paths

The simplest reliable way to rotate IPs in Puppeteer is to use a provider-managed rotating gateway or per-BrowserContext proxies paired with sticky sessions, not to swap proxies per request inside your script. Proxy authentication needs page.authenticate or a local tunnel, since Chromium won't accept credentials in the launch flag, and SOCKS5 behaves differently from HTTP(S). The code examples below cover both approaches.


TL;DR:

  • Using a provider-managed rotating gateway is the simplest and most scalable option for most scraping tasks, with automatic IP rotation and minimal maintenance.
  • Assigning a different proxy to each BrowserContext balances session isolation with resource efficiency, especially when handling multiple simultaneous sessions.
  • Connecting to a rotating gateway with a static proxy URL still allows IP changes without code modifications, but requires careful immediate proxy authentication after creating each page.
  • Proxy credentials cannot be embedded directly in the --proxy-server flag; instead, use page.authenticate for HTTP and SOCKS5 proxies to avoid common connection errors.
  • MaskLabs offers real US mobile carrier IPs with city-level targeting supporting both sticky and rotating sessions, ideal for overcoming advanced anti-bot detection.

Table of Contents

Three practical methods to apply proxies in Puppeteer and how to choose between them

Most Puppeteer proxy setups fall into three patterns, and picking the right one depends on how much concurrency and session persistence your scraper needs.

  • Provider-managed rotating gateway: you point Puppeteer at a single gateway endpoint and the provider rotates the exit IP behind the scenes, which is the lowest-maintenance option and scales cleanly.
  • One proxy per BrowserContext: you assign a different proxy to each context inside the same browser instance, which gives you session isolation without the overhead of separate Chromium processes.
  • One browser per proxy: you launch a full browser instance for each proxy, which gives strict isolation but consumes far more memory and CPU per concurrent task.

A gateway is the right default for most scraping jobs because it removes the need to track proxy health, retire dead IPs, or write your own rotation logic. BrowserStack's Puppeteer proxy guide notes that swapping proxies per request through request interception is fragile and tends to add latency, which is why provider-side rotation or context-level assignment is the more durable pattern.

BrowserContexts earn their place when you need several sessions running in parallel with different exit IPs, but don't want the resource cost of separate browsers. The BrowserContext class documentation confirms that each context keeps its own isolated storage, so cookies and cache from one proxy route never leak into another running in the same browser.

The one-browser-per-proxy approach still has a place when you're dealing with anti-bot systems sensitive to browser-level fingerprints shared across contexts, but it's the heaviest option on memory and rarely worth it for standard scraping at scale. For most teams, the decision comes down to this: start with a gateway for throughput, add BrowserContexts when you need isolated, persistent sessions running side by side.

Copy-ready code: launch args, BrowserContext proxyServer, and integrating a rotating gateway

Here are three working patterns, from simplest to most flexible.

  1. Minimal launch with --proxy-server. Pass the proxy directly to puppeteer.launch, then verify the exit IP before doing anything else.
const browser = await puppeteer.launch({
  args: ['--proxy-server=http://gateway.example.com:8000']
});
const page = await browser.newPage();
await page.authenticate({ username: 'user', password: 'pass' });
await page.goto('https://api.ipify.org?format=json');
console.log(await page.content());
  1. BrowserContext with proxyServer for isolated sessions. The proxyServer property on BrowserContextOptions lets you assign a proxy per context rather than per browser, which matters when you're running several routes at once.
const browser = await puppeteer.launch();
const context = await browser.createBrowserContext({
  proxyServer: 'http://gateway.example.com:8001'
});
const page = await context.newPage();
await page.authenticate({ username: 'user', password: 'pass' });
await page.goto('https://api.ipify.org?format=json');

You can repeat this for as many contexts as you need, each with its own proxy, all inside a single browser process.

  1. Rotating gateway with no flag changes. If your provider rotates IPs on its side, your Puppeteer code stays static. You connect to one endpoint, and each new connection (or each request, depending on the provider's rotation interval) comes back through a different exit IP without you touching launch args again.
const context = await browser.createBrowserContext({
  proxyServer: 'http://rotating.gateway.example.com:9000'
});
const page = await context.newPage();
await page.authenticate({ username: 'user', password: 'pass' });
await page.goto('https://api.ipify.org?format=json');

Place page.authenticate immediately after newPage() and before any goto call. It has to run before navigation or the first request goes out unauthenticated and fails. Checking api.ipify.org or a similar IP-echo endpoint after each context is created is the fastest way to confirm you're actually exiting through the proxy you configured, not your real IP.

How to authenticate proxies reliably and protocol-specific gotchas

Chromium will not accept a username:password pair embedded directly in the --proxy-server flag. Trying it is one of the most common sources of ERR_NO_SUPPORTED_PROXIES, confirmed repeatedly in Puppeteer's own issue tracker. The supported path is page.authenticate, which has to be called per page (or applied to every page opened from a given context) before navigation starts.

  • Never write --proxy-server=http://user:pass@host:port. Use --proxy-server=http://host:port and authenticate separately.
  • For SOCKS5, use --proxy-server=socks5://host:port and still call page.authenticate for credentials, since SOCKS5 doesn't support inline auth in the flag either.
  • Watch for stray quotes or extra spaces around the flag value, which produces malformed proxy strings that Chromium rejects outright.
  • Remember that page.authenticate only covers HTTP-based proxy authentication; some SOCKS5 providers require IP allowlisting instead of credentials.

Pro Tip: Run a local unauthenticated tunnel with proxy-chain in front of your authenticated upstream proxy. Chromium talks to the local tunnel with no credentials involved, which sidesteps a whole category of auth bugs when you're managing many pages at once.

Maintainable rotation strategies and session persistence best practices

The most maintainable rotation setup is one you barely have to think about: a provider-managed gateway that rotates exit IPs automatically and handles dead-proxy replacement without you writing health-check logic. This is the pattern worth defaulting to unless you have a specific reason to manage IPs yourself.

  • Use a rotating gateway for high-volume, stateless scraping where each request or short session doesn't need to keep the same IP.
  • Use sticky sessions paired with a dedicated BrowserContext when a workflow requires staying logged in or maintaining a consistent geo-location across multiple pages, a pattern covered in more detail in MaskLabs's guide to sticky sessions for account workflows.
  • Keep cookies, localStorage, and custom headers scoped to the same context as the proxy they were built under. Mixing a rotated IP with a session's old cookies is a fast way to get flagged.
  • Add exponential backoff and retry logic around failed requests instead of hammering the same proxy repeatedly, which reduces the odds of getting an IP range blocked outright.

Choosing between sticky and rotating isn't always obvious from the outset, and it's worth reading through a breakdown of when each session type actually fits the job before committing your architecture to one pattern. Rotation frequency matters too: rotating too aggressively on a login-gated site breaks sessions, while never rotating on a high-volume scrape invites rate limiting. Match the rotation interval to the task, not the other way around.

Debug checklist for common failures: ERR_NO_SUPPORTED_PROXIES, TLS issues and timeouts

  1. Test the proxy outside Puppeteer first. A quick curl request or a small Node HTTP call against the proxy confirms it works before you add Chromium into the mix.
  2. Strip credentials from the --proxy-server flag. If you see ERR_NO_SUPPORTED_PROXIES, this is the first thing to check, since community threads on the issue point to embedded credentials as the leading cause.
  3. Test HTTP before HTTPS. TLS handshake failures through a proxy are easier to isolate once you know plain HTTP traffic passes.
  4. Check the proxy type string. socks5:// typed as socks:// or a missing protocol prefix will also throw ERR_NO_SUPPORTED_PROXIES.
  5. Watch memory and CPU when launching many browsers. Each full browser instance carries real overhead, which is why Puppeteer's BrowserContext documentation recommends multiple contexts inside one browser for concurrent proxy routes instead of one browser per task.

Managed rotation gateways remove the need to track individual proxy health and cycling logic, which is one reason teams scaling past a handful of concurrent sessions move away from manually maintained IP lists.

When mobile carrier IPs, sticky sessions, and provider-managed rotation matter

When mobile carrier IPs, sticky sessions, and provider-managed rotation matter — overview diagram

Datacenter IPs get flagged faster on sites that specifically watch for non-residential ranges, which is where real carrier IPs change the math. MaskLabs routes traffic through genuine US mobile carrier IPs rather than datacenter ranges, which can reduce the odds of blocking or throttling on targets that are more aggressive about datacenter detection.

MaskLabs also supports both sticky and rotating sessions with city-level exit selection across 46 US cities, which matters for scraping or testing that depends on a consistent, verifiable location rather than a generic country-level IP. That kind of geo-precision is also useful outside pure scraping, including geo-targeted testing in browser automation frameworks.

Real carrier IPs, sticky or rotating by choice, and city-level targeting across 46 US cities.

MaskLabs states a roughly 99.9% uptime and a success rate over 99% for its proxy network, according to MaskLabs.

Author's rules of thumb and recommendations

Start with a provider-managed gateway. It's the least code, the least maintenance, and it scales without you touching rotation logic again. Move to BrowserContexts with sticky sessions only once you hit a workflow that genuinely needs session persistence, like a login flow or a multi-step checkout you're testing.

Author's rules of thumb and recommendations — overview diagram

Before scaling any scraper, test with a small data budget and measure your success rate against the actual target site, not against a generic IP checker. A proxy that resolves cleanly on api.ipify.org can still get blocked on a specific, more defensive target.

Build IP verification and automated retries into the scraper from day one. Treat them as core logic, not an afterthought you bolt on after the first block.

— Jon

Try MaskLabs: rotating and sticky mobile proxy options

Masklabs

If you're running the patterns above and want the underlying IPs to hold up against tougher anti-bot systems, MaskLabs gives you real carrier IPs instead of datacenter ranges, with the choice to rotate or stay sticky depending on what your workflow needs.

  • Real US mobile carrier IPs with city-level targeting across 46 cities.
  • Sticky sessions for login-dependent workflows, rotating sessions for high-volume scraping.
  • Full HTTP, HTTPS, and SOCKS5 support with self-serve credential management and API access.

MaskLabs offers subscription plans at various price points; current prices are available on the MaskLabs pricing page. You can select a plan, run a small test pool against your target site, and monitor your success rate before upgrading.

Key docs and threads to validate details

Sources

FAQ

What is the best way to rotate proxies in Puppeteer?

The most reliable method is to use a provider-managed rotating gateway or assign a proxy per BrowserContext, rather than swapping proxies per request inside your script. This keeps your code stable while the provider or context handles the IP switching.

Why does Puppeteer throw ERR_NO_SUPPORTED_PROXIES?

This error most often comes from embedding a username and password directly in the --proxy-server flag, which Chromium doesn't support. Removing the credentials from the flag and using page.authenticate instead, as documented in Puppeteer's issue tracker, resolves it in most cases.

How do I use a SOCKS5 proxy with Puppeteer?

Set the flag as --proxy-server=socks5://host:port and pass any required credentials through page.authenticate rather than inline in the URL. Malformed protocol prefixes are a common cause of connection failures with SOCKS5 setups.

Should I use one BrowserContext per proxy or one browser per proxy?

BrowserContexts are generally more efficient because they isolate sessions and storage without the memory and CPU cost of launching a full browser for every proxy. Reserve a separate browser instance per proxy for cases where you need stricter isolation than context-level separation provides.

Does MaskLabs support proxy rotation in Puppeteer?

MaskLabs supports both rotating and sticky sessions over real US mobile carrier IPs, which work with Puppeteer through standard HTTP, HTTPS, or SOCKS5 configuration. Current pricing details are available on the MaskLabs pricing page.

Recommended