Puppeteer Proxy Setup: Authentication, Sessions, and Testing

Configure a proxy in Puppeteer with a Chromium launch argument, supply credentials safely, verify the exit IP, and diagnose the layer that failed.

Written by the Mexela Editorial Team. Technical guides are reviewed by the Mexela Technical Team under the Mexela Editorial Policy.

Node.js Puppeteer browser automation routed through a private proxy server

Key topics:

PROXY PLANS

Ready to buy proxies for this workflow?

Use the guide below to choose the right proxy type, then start with private proxies for dedicated IPv4 access or shared proxies when price matters more.

To use a proxy with Puppeteer, pass the proxy endpoint to Chromium through the --proxy-server launch argument, create the page, and call page.authenticate() before navigation when the HTTP proxy requires a username and password. Verify the observed exit from inside that same page before starting the real workflow.

Scope: this tutorial covers one HTTP or HTTPS-tunneling proxy per browser process in Node.js. For generic Node clients and curl, use the multi-client proxy guide; for the full protocol distinction, use the Proxy Setup and Developer Guides hub.

Start Chromium with a proxy launch argument

Puppeteer passes additional browser flags through the args property documented in the official Puppeteer LaunchOptions reference. Keep the endpoint separate from credentials so error messages, process listings, and copied commands do not reveal a password.

import puppeteer from 'puppeteer';

const PROXY_HOST = 'HOST';
const PROXY_PORT = 'PORT';

const browser = await puppeteer.launch({
  headless: true,
  args: ['--proxy-server=' + 'http://' + PROXY_HOST + ':' + PROXY_PORT],
});

const page = await browser.newPage();

This launch argument applies at browser-process level. Opening another page in the same process does not create a different upstream proxy. If two workers must use different endpoints, launch two browser processes or use an architecture that explicitly supports per-context routing.

Add proxy authentication before the first navigation

The official page.authenticate() reference describes credential handling and notes that authentication enables request interception internally. Call it before page.goto(). Read secrets from a protected runtime store; do not commit them to a JavaScript file.

const PROXY_USER = 'USER';
const PROXY_PASSWORD = 'PASSWORD';

await page.authenticate({
  username: PROXY_USER,
  password: PROXY_PASSWORD,
});

A 407 response usually means the proxy was reached but rejected the presented proxy authentication. Check the assigned method, credential spelling, account status, and whether source-IP authorization is expected instead. The proxy authentication guide separates those models.

Perform exit verification inside Puppeteer

Use a trusted HTTPS endpoint that returns the requester’s address, then compare it with a direct baseline. Inspect status and content type before parsing the body; an HTML challenge page is not an IP result.

const response = await page.goto('https://api.ipify.org?format=json', {
  waitUntil: 'domcontentloaded',
  timeout: 30000,
});

if (!response || !response.ok()) {
  throw new Error(`Exit check failed: ${response?.status() ?? 'no response'}`);
}

const observed = await page.evaluate(() => JSON.parse(document.body.innerText));
console.log({ exitIp: observed.ip });

Expected observation: the page reports the assigned proxy exit, HTTPS validation remains enabled, and a second request succeeds with the same endpoint when the plan is static. If the direct IP remains visible, stop and inspect the browser launch arguments rather than continuing the automation.

Design session isolation deliberately

Session isolation means keeping cookies, cache, credentials, and one intended egress together for the life of a workflow. Use a separate browser context for separate application sessions, but remember that contexts inside one Chromium process still inherit its process-level proxy.

const context = await browser.createBrowserContext();
const sessionPage = await context.newPage();
await sessionPage.authenticate({ username: PROXY_USER, password: PROXY_PASSWORD });
// Perform one authorized session, then close its context.
await context.close();

Do not rotate the address halfway through a login or transaction simply because a request failed. First classify the failure. Changing the egress can invalidate state and make a reproducible bug look intermittent.

Puppeteer troubleshooting by symptom

Symptom Likely boundary First check
ERR_PROXY_CONNECTION_FAILED Gateway DNS, port, route, or listener Test the documented host and port once outside Puppeteer
407 response Authentication Check method, credentials, and source-IP allowlist
Timeout after connection Tunnel, TLS, or destination Compare a known exit endpoint with the real target
Direct IP still visible Launch configuration Log the non-secret endpoint label and actual browser args
Only one website fails Destination policy or application state Preserve status and response class; do not retry blindly

The layered sequence in Common Proxy Errors and Fixes helps separate connectivity, DNS, TLS, and destination responses.

Operational limits: run automation only on systems and accounts you are authorized to test, follow destination terms and rate limits, cap concurrency, and redact cookies, tokens, and proxy credentials from screenshots and logs. A proxy changes a route; it does not grant access.

Choose an endpoint after the Puppeteer test is reproducible

Record the required country, protocol, authentication method, session duration, browser count, and measured latency. If the workflow needs a stable exclusive egress, compare those requirements with the current private proxy options and start with a small controlled allocation.