Web Scraping & Automation

Puppeteer Proxy Authentication: Every Method Explained

How to use authenticated proxies in Puppeteer: page.authenticate(), IP whitelisting, proxy-chain local forwarders, per-context credentials, SOCKS5 auth workarounds and fixing 407 errors.

By Team of Monkeys Editorial Updated 4 min read Affiliate disclosure

This guide contains affiliate links to Geonode. If you buy through them we may earn a commission, at no extra cost to you.

Why authentication is the first Puppeteer proxy hurdle

Nearly every paid proxy requires authentication, and nearly everyone's first attempt in Puppeteer looks like this:

javascript
// This does NOT work
const browser = await puppeteer.launch({
  args: ['--proxy-server=http://USER:PASS@gate.example.com:7777'],
});

Chrome ignores credentials in the --proxy-server flag. The result is a 407 Proxy Authentication Required error, an ERR_INVALID_AUTH_CREDENTIALS, or an authentication dialog in headful mode. This guide covers the methods that do work. For the full Puppeteer proxy picture, see our Puppeteer proxies pillar guide.

Method 1: page.authenticate()

The standard approach. Launch with the proxy host and port only, then give each page the credentials:

javascript
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  args: ['--proxy-server=http://gate.example.com:7777'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USER', password: 'PASS' });
await page.goto('https://httpbin.org/ip', { waitUntil: 'domcontentloaded' });
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();

How it works: when Chrome receives a 407 challenge from the proxy, Puppeteer intercepts it and responds with your credentials.

Things to know:

  • Call it on every page before the first navigation. New pages, including popups, don't inherit credentials automatically, so handle targetcreated events if your flow opens new tabs.
  • It works for HTTP/HTTPS proxies, not SOCKS5.
  • Changing credentials on an existing page (for example, to switch session IDs) may not take effect immediately because Chrome caches proxy auth and reuses connections. For a fresh IP, use a new browser context or browser.

Handling popups:

javascript
browser.on('targetcreated', async (target) => {
  if (target.type() === 'page') {
    const p = await target.page();
    if (p) await p.authenticate({ username: 'USER', password: 'PASS' });
  }
});

Method 2: IP whitelisting

If your server has a fixed public IP, add it to your provider's whitelist. Then no credentials are needed:

javascript
const browser = await puppeteer.launch({
  args: ['--proxy-server=http://gate.example.com:7777'],
});

Pros: simplest code, works with SOCKS5, no credential handling. Cons: only works from whitelisted IPs, and targeting must be set by port or dashboard rather than username. See proxy authentication methods.

Method 3: proxy-chain local forwarder

The proxy-chain package starts a local proxy without authentication that forwards to your upstream proxy with credentials:

javascript
import puppeteer from 'puppeteer';
import proxyChain from 'proxy-chain';

const upstream = 'http://customer-USER-country-us-session-abc123:PASS@gate.example.com:7777';
const local = await proxyChain.anonymizeProxy(upstream); // e.g. http://127.0.0.1:41235

const browser = await puppeteer.launch({ args: [`--proxy-server=${local}`] });
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
console.log(await page.evaluate(() => document.body.innerText));

await browser.close();
await proxyChain.closeAnonymizedProxy(local, true);

Use this when:

  • You need authenticated SOCKS5 (proxy-chain can forward to SOCKS upstreams in recent versions).
  • You want credentials out of Puppeteer code paths entirely.
  • You use tools on top of Puppeteer that don't expose page.authenticate().

Method 4: Per-context proxies with credentials

Recent Puppeteer versions let each browser context use its own proxy server. Combine with page.authenticate() on pages in that context:

javascript
const browser = await puppeteer.launch();
const ctx = await browser.createBrowserContext({ proxyServer: 'http://gate.example.com:7777' });
const page = await ctx.newPage();
await page.authenticate({ username: 'customer-USER-session-1', password: 'PASS' });
await page.goto('https://httpbin.org/ip');

This is the cleanest way to run several identities in one browser. See Puppeteer proxy per page.

Special characters in passwords

With page.authenticate(), pass the raw password. With URL-based configuration (proxy-chain), URL-encode special characters:

javascript
const pass = encodeURIComponent('p@ss:w/rd');
const upstream = `http://USER:${pass}@gate.example.com:7777`;

Proxy tip: If you need a pool to run this against, Geonode works with every tool in this guide: rotate per request for stateless pages, or hold a sticky session for logins and pagination. See using Geonode with Puppeteer for working code.

Troubleshooting authentication

SymptomLikely causeFix
407 or ERR_INVALID_AUTH_CREDENTIALSCredentials in flag, wrong password, authenticate() not calledUse page.authenticate() before goto()
Works on first page, fails on popupsNew pages lack credentialsHandle targetcreated
Session ID change has no effectConnection reuse / auth cacheNew context or browser per session
SOCKS5 with credentials failsChrome lacks SOCKS5 authWhitelist IP or use proxy-chain
Works locally, fails on serverServer IP not whitelistedUpdate whitelist

More: common proxy errors.

Rotating credentials safely

When you rotate session IDs in the username, follow this sequence for reliable results:

  1. Create a new browser context (or browser).
  2. Open a page in it.
  3. Call page.authenticate() with the new session username.
  4. Navigate.
  5. Close the context when finished.

Reusing a context after changing credentials risks Chrome reusing a pooled connection authenticated with the old session.

Keeping credentials out of code

javascript
const { PROXY_HOST, PROXY_USER, PROXY_PASS } = process.env;
const browser = await puppeteer.launch({ args: [`--proxy-server=${PROXY_HOST}`] });
const page = await browser.newPage();
await page.authenticate({ username: PROXY_USER, password: PROXY_PASS });

Use environment variables or a secrets manager, and mask credentials in logs.

Authentication with request interception

If you use page.setRequestInterception(true) for resource blocking, page.authenticate() still works. Call authenticate() before enabling interception and navigating, and make sure your request handler always calls continue() or abort() for every request, including the initial navigation.

Checking that authentication succeeded

Navigate to an IP echo endpoint and confirm the IP isn't yours:

javascript
await page.goto('https://api.ipify.org');
console.log('exit IP:', await page.evaluate(() => document.body.innerText));

If you see your own IP, the proxy isn't being applied. If navigation fails with an auth error, credentials are wrong or not being sent. See how to check if a proxy works.

The hidden cost of page.authenticate()

page.authenticate() is the easiest method, but it isn't free. Puppeteer's own documentation notes that request interception is turned on behind the scenes to implement authentication, and that this might affect performance. Every request the page makes passes through that interception layer.

For a handful of pages, you won't notice. At scale, it can matter:

SituationBetter option
Fixed servers with stable IPsIP whitelisting: no credentials, no interception
Many pages per browser, heavy pagesA local forwarder (such as proxy-chain) that adds credentials outside the browser
Per-context proxies with different userspage.authenticate() per page is still the simplest; measure before optimising
SOCKS5 with credentialsA forwarder, since Chrome can't authenticate to SOCKS5

To see whether it matters for you, time the same batch of pages twice: once with page.authenticate() and once through a whitelisted endpoint or a local forwarder. Compare median page load time and CPU use. If the difference is small, keep the simple approach.

The method signature and notes are on the official Page.authenticate reference. For forwarders, see Method 3 above and proxy chains; for whitelisting, proxy authentication methods. And if you're scaling up, scaling Puppeteer with puppeteer-cluster covers worker design.

Key takeaways

  • Chrome ignores credentials in --proxy-server; use page.authenticate(), IP whitelisting or a local forwarder.
  • Call page.authenticate() on every new page, including popups, before navigation.
  • For new session IDs, use a new browser context or browser to avoid reused connections.
  • SOCKS5 authentication isn't supported by Chrome; use whitelisting or proxy-chain.
  • Keep credentials in environment variables and mask them in logs. See puppeteer proxies.

FAQ

Can I put credentials in the --proxy-server flag?

No, Chrome ignores them.

Does page.authenticate() work with SOCKS5?

No. Use IP whitelisting or a local forwarder. See Puppeteer SOCKS5 proxy.

Which method is best?

IP whitelisting on fixed servers; page.authenticate() otherwise; proxy-chain for SOCKS5 or complex setups.

Affiliate disclosure: Team of Monkeys is a Geonode affiliate. Links and banners marked as sponsored, and links to Geonode in the text, use our referral link, and we may earn a commission if you buy through them, at no extra cost to you. Our explanations and comparisons are written to be useful whichever provider you choose.

Editorial note: Proxy providers change their pools, features and prices often, so treat any figures here as typical ranges at the time of writing and confirm current terms with the provider before you buy.