Puppeteer Proxy: How to Use Authenticated Proxies in Puppeteer

Developer guide · Last reviewed 27 September 2026 · 10 min read

A Puppeteer proxy takes two things: the proxy address, which Chrome gets as a launch flag, and the username and password, which Puppeteer answers when the proxy asks. This guide shows the working setup with --proxy-server and page.authenticate(), how to give each browser context its own proxy, how to rotate IPs or keep sticky sessions with rotating residential proxies, and how to fix the errors people hit most often.

The short version

Launch Chrome with --proxy-server=http://HOST:PORT, then call await page.authenticate({ username, password }) before page.goto(). Don’t put the username and password in the proxy URL: Chrome ignores them there. For a different proxy per session, create a browser context with its own proxyServer. Chrome can’t use SOCKS5 with a password, so use the HTTP endpoint for authenticated proxies.

How Puppeteer Uses a Proxy

Chrome connects, Puppeteer answers the login

Puppeteer drives a real Chrome or Chromium browser, and it’s the browser, not your Node.js code, that opens the network connections. So the proxy has to be given to the browser. There are two places to do it: the --proxy-server command-line flag when you launch Chrome, which applies to the whole browser, or the proxyServer option when you create a browser context, which applies to that context only.

Authentication is a separate step. When Chrome connects to a proxy that needs a login, the proxy replies with 407 Proxy Authentication Required, and the browser has to answer with credentials. In a normal Chrome window that would be a sign-in box. In Puppeteer, page.authenticate() supplies the username and password so the page can load without anyone typing them.

Where each part of the proxy goes
puppeteer.launch({ args: ['--proxy-server=http://HOST:PORT'] })
        |
        v
   Chrome  ----- CONNECT ---->  proxy (asks for a login: 407)
        ^                            |
        |                            v
page.authenticate({ username, password })  -->  target website

The Puppeteer documentation describes page.authenticate() simply as providing credentials for HTTP authentication, and notes that request interception is turned on behind the scenes to do it, which can affect performance. For most scraping and testing jobs that cost is small.

Puppeteer Proxy with Authentication: the Standard Setup

Launch flag plus page.authenticate()

This is the setup that works for almost everyone. Get your host, port, username and password from the Proxy Manager in your ProxyEmpire dashboard, then:

Node.js — Puppeteer proxy with a username and password
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({
  args: ['--proxy-server=http://v2.proxyempire.io:5000'],
});

const page = await browser.newPage();

// Answer the proxy's login request for this page
await page.authenticate({
  username: process.env.PROXY_USER,
  password: process.env.PROXY_PASS,
});

await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));

await browser.close();

A few details that save time:

  • Call page.authenticate() on every page, before its first goto(). A new tab opened with browser.newPage() needs its own call.
  • Keep credentials out of your code. Read them from environment variables or a secrets manager. Anyone with your proxy username and password can spend your bandwidth.
  • Use the IP check to confirm it works. The response should show an address and location from the proxy, not your own connection.
  • Your username encodes your targeting. With ProxyEmpire, the username changes when you change country, city or session settings, so copy it again from the dashboard after any change.

Putting the credentials in the URL, as in --proxy-server=http://user:pass@host:port, is the most common mistake. Chrome doesn’t accept credentials in that flag, so the proxy keeps returning a 407 and pages fail to load.

A Different Proxy for Each Browser Context

One browser, many identities

Launching a new browser for every proxy is slow. Puppeteer’s browser.createBrowserContext() creates an isolated context that doesn’t share cookies or cache with other contexts, and its proxyServer option gives that context its own proxy. The Puppeteer docs note that the username and password for it are set with page.authenticate(), the same as before.

Node.js — two contexts, two proxy sessions
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();

async function openWithProxy(username) {
  const context = await browser.createBrowserContext({
    proxyServer: 'http://v2.proxyempire.io:5000',
  });
  const page = await context.newPage();
  await page.authenticate({ username, password: process.env.PROXY_PASS });
  return { context, page };
}

const a = await openWithProxy(process.env.PROXY_USER_US);
const b = await openWithProxy(process.env.PROXY_USER_DE);

await a.page.goto('https://example.com');
await b.page.goto('https://example.com');

await a.context.close();
await b.context.close();
await browser.close();

Because the ProxyEmpire username carries the targeting and session settings, two contexts with two usernames can browse from two different countries, or two different sticky IPs, in the same browser. createBrowserContext() is the current name for what older Puppeteer versions called createIncognitoBrowserContext(). If you are on an old version, upgrade or use the older name.

The proxy-chain Alternative

When page.authenticate() isn’t an option

Some setups can’t call page.authenticate(): service workers and some pop-ups, tools that wrap Puppeteer, or code that needs to pass one proxy URL to Chrome and forget about it. The open-source proxy-chain package solves this. Its anonymizeProxy() function starts a small local proxy with no password that forwards everything to your authenticated proxy, so Chrome only ever sees a local address.

Node.js — proxy-chain in front of an authenticated proxy
import puppeteer from 'puppeteer';
import { anonymizeProxy, closeAnonymizedProxy } from 'proxy-chain';

const upstream = `http://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@v2.proxyempire.io:5000`;
const localProxy = await anonymizeProxy(upstream);   // e.g. http://127.0.0.1:12345

const browser = await puppeteer.launch({ args: [`--proxy-server=${localProxy}`] });
const page = await browser.newPage();                 // no page.authenticate() needed
await page.goto('https://api.ipify.org?format=json');

await browser.close();
await closeAnonymizedProxy(localProxy, true);

If your password contains characters such as @, : or /, URL-encode it before building the upstream URL. Remember to close the local proxy when you’re done, or it keeps running in your process.

Rotating IPs and Sticky Sessions in Puppeteer

Match the session to the job

With a rotating proxy endpoint, you choose the behaviour in the dashboard rather than in your code:

New IP per requestEvery request can leave from a different IP. Best for collecting many independent pages, such as search results or product listings.
Sticky sessionThe same IP stays with the session until you rotate it or the device behind it goes offline. Best for logins, carts and multi-step flows.
Custom rotation timerKeep an IP for a set time, then rotate. A middle ground for long crawls of one site.
TargetingCountry, region, city, ZIP, ISP or mobile carrier, ASN and OS fingerprint, included at no extra charge.

For a browser, sticky sessions are usually the right default. A single page load pulls in scripts, images and API calls, and if those come from different IPs, some sites treat the visit as suspicious. Use one sticky session per browser context, and switch to a new session, by using a new username from the dashboard, when you want a fresh identity.

Save bandwidth in Puppeteer

  • Block images, fonts and media you don’t need with request interception, since you pay per GB of proxy traffic.
  • Reuse a context for several pages on the same site instead of starting from zero each time.
  • Close contexts and pages as soon as you’re done with them, so background requests stop.
  • Where a site offers the same data in its HTML or a public API, a plain HTTP request is cheaper than a full browser.

Running many sessions in parallel

Scraping jobs often run dozens of pages at once. The pattern that scales is one browser, many contexts: each context gets its own proxy username and its own cookies, and a small pool of contexts works through a queue of URLs. ProxyEmpire has no limit on concurrent sessions, so the practical limit is your machine’s memory and CPU, not the proxy. Keep an eye on both, since every Chrome context and tab uses real resources.

Give each worker a clear lifecycle: create the context, authenticate its pages, process a batch of URLs, then close the context and start a fresh one with a new session. Recycling contexts regularly keeps memory in check and gives you a new IP at predictable points instead of in the middle of a job.

Checking that the proxy is really used

Before a long run, load an IP-check page in each context and log the result, as in the first example. If a context prints your own address, its proxy wasn’t applied; if it prints nothing, the login probably failed. A second quick check is to confirm the country matches the targeting you chose, since a working proxy in the wrong location still ruins location-specific data.

SOCKS5 and Puppeteer

Chrome can’t send a SOCKS5 password

Chrome accepts SOCKS proxies in --proxy-server, but it has no way to send a username and password to a SOCKS5 proxy. The feature request for SOCKS5 authentication in the Chromium issue tracker is closed without being implemented. If you pass an authenticated SOCKS5 proxy to Puppeteer, requests fail or the credentials are dropped.

The fix is simple: use the HTTP endpoint. ProxyEmpire supports HTTP, HTTPS and SOCKS5 on the same account, and HTTP with page.authenticate() still carries HTTPS traffic through an encrypted tunnel to the target site. If you really need SOCKS5 in the browser, proxy-chain can sit in the middle so Chrome talks to a local proxy without a password.

Troubleshooting Puppeteer Proxy Errors

The errors we see most
What you seeLikely cause and fix
ERR_TUNNEL_CONNECTION_FAILEDThe proxy refused the tunnel, most often because of a missing or wrong login. Check that page.authenticate() runs before goto() and that the username matches your current targeting.
ERR_INVALID_AUTH_CREDENTIALSThe username or password is wrong, or was copied with a space. Copy both again from the Proxy Manager.
ERR_PROXY_CONNECTION_FAILEDChrome can’t reach the proxy at all. Check the host and port, and that nothing on your network blocks outgoing connections to it.
Page shows your own IPThe proxy flag didn’t apply. Check the args array, and that you didn’t create the context without proxyServer.
Works for the first page, fails for new tabsEach new page needs its own page.authenticate() call.
Some sites never loadFinancial, government and other high-risk sites are blocked on the network, and only ports 80 and 443 are open by default.
Everything stops at onceYour bandwidth ran out. Check the balance in the dashboard and top up.

If none of these fit, the proxy troubleshooting guide in our docs covers the rest, and the 24/7 live chat is answered by real people who can look at your setup with you.

Which Proxies to Use with Puppeteer

Match the IP type to the site

Puppeteer is often used on the sites that are hardest to reach, the ones that need JavaScript and check visitors closely. That makes the IP type matter.

  • Rotating residential proxies come from real home broadband connections in 179 countries and territories, with targeting down to city, ZIP, ISP and ASN. They are the default choice for browser automation, from $3.50 for one GB down to $1.50/GB.
  • Rotating mobile proxies use 4G carrier networks in 174 countries, from $2.00/GB. Use them for mobile sites, app back ends and anything that treats phones differently.
  • Datacenter proxies are cheapest, from $0.35/GB, and fine for sites without strong bot checks.

All three work with the setup in this guide, and unused bandwidth rolls over on every plan. For more on running browsers at scale, see our guide to web automation with Chromium.

Puppeteer Proxy FAQ

Quick answers
How do I set a proxy in Puppeteer?

Pass --proxy-server=http://HOST:PORT in the args of puppeteer.launch(), or set proxyServer when you call browser.createBrowserContext() to give one context its own proxy.

How do I add a proxy username and password in Puppeteer?

Call await page.authenticate({ username, password }) on each page before it navigates. Don’t put the credentials in the --proxy-server URL, because Chrome ignores them there.

Can I use a different proxy for each page?

Use a separate browser context per proxy. Pages in the same context share its proxy; each context can have its own proxyServer and its own username.

Does Puppeteer support SOCKS5 proxies?

Chrome supports SOCKS5 without a password, but it can’t authenticate to a SOCKS5 proxy. For authenticated proxies, use the HTTP endpoint, or put proxy-chain in front of the SOCKS5 proxy.

Why do I get ERR_TUNNEL_CONNECTION_FAILED?

Usually the proxy rejected the login. Make sure page.authenticate() runs before page.goto(), and that the username matches the targeting you set in the dashboard.

Does page.authenticate() slow Puppeteer down?

It turns on request interception behind the scenes, which adds a little overhead. For most jobs the difference is small. If it matters, proxy-chain avoids it by handling the login outside the browser.

Does this work with Playwright too?

The idea is the same, but Playwright takes the proxy and its username and password together in the proxy option of launch() or newContext(). The ProxyEmpire host, port and credentials are the same.

References

Sources and documentation
  1. Puppeteer — Page.authenticate(). pptr.dev/api/puppeteer.page.authenticate
  2. Puppeteer — Browser.createBrowserContext(). pptr.dev/api/puppeteer.browser.createbrowsercontext
  3. Puppeteer — BrowserContextOptions (proxyServer, proxyBypassList). pptr.dev/api/puppeteer.browsercontextoptions
  4. Chromium — Network settings (the --proxy-server flag). chromium.org/developers/design-documents/network-settings
  5. Chromium issue tracker — SOCKS5 authentication support. issues.chromium.org/issues/40323993
  6. Apify — proxy-chain (anonymizeProxy, closeAnonymizedProxy). github.com/apify/proxy-chain
  7. MDN — 407 Proxy Authentication Required. developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/407

Run Puppeteer on residential and mobile IPs

Test the setup in this guide with the $1.97 trial: 100 MB of residential and 50 MB of mobile bandwidth, sticky or rotating sessions, targeting down to city and ISP, and 24/7 support from real people.

Flexible Pricing Plan

logo purple proxyempire

Our state-of-the-art proxies.

Experience online freedom with our unrivaled web proxy solutions. Pioneering in collecting location specific data at scale, our premium, ethically-sourced network boasts a vast pool of IPs, expansive location choices, high success rate, and versatile pricing. Advance your digital journey with us.

🏘️ Rotating Residential Proxies
  • 30M+ Premium Residential IPs
  •  170+ Countries
    Every residential IP in our network corresponds to an actual desktop device with a precise geographical location. Our residential proxies are fast and reliable, with 99.9% uptime, and work for a wide range of use cases. You can use Country, Region, City and ISP targeting for our rotating residential proxies.

See our Rotating Residential Proxies

📍 Static Residential Proxies
  • 24 Countries
    Buy a dedicated static residential IP from one of the 24 countries that we offer proxies in. Keep the same IP for a month or longer, while benefiting from their fast speed and stability.

See our Static Residential Proxies

📳 Rotating Mobile Proxies
  • 4M+ Premium Mobile IPs
  •  170+ Countries
    Access millions of clean mobile IPs with precise targeting including Country, Region, City, and Mobile Carrier. Get far fewer IP blocks and CAPTCHAs with our 4G and 5G proxies.

See our Mobile Proxies

📱 Dedicated Mobile Proxies
  • 5+ Countries
  • 50+ Locations
    Get your own dedicated mobile proxy in one of our supported locations, with unlimited bandwidth and unlimited IP changes on demand. A great choice when you need a small number of mobile IPs and a lot of proxy bandwidth.

See our 4G & 5G Proxies

🌐 Rotating Datacenter Proxies
  • 197,000+ IPs Premium IPs
  •  62 Countries
    On a budget and need to do some simple scraping tasks? Our datacenter proxies are the perfect fit! Get started with as little as $2

See our Datacenter Proxies

proxy locations

30M+ rotating IPs

99% uptime - high speed

99.9% uptime.

dedicated support team

24/7 Dedicated Support.

fair price

Fair Pricing.

🏠 Residential Proxies Rotating / Static / Unlimited
📱 Mobile Proxies Rotating and Dedicated
🖥️ Datacenter Proxies Rotating
🌍 IP Pool 30M+ residential + 4M+ mobile IPs
📶 Uptime 99.9% · Live status
💳 Payment Card · PayPal · Crypto · Bank transfer
💬 Support 24/7 live chat · [email protected]