Quick answer
How do I use an authenticated proxy in Puppeteer?
Launch Chrome with --proxy-server=http://gw.crawlrelay.com:7777, then call page.authenticate({ username, password }) before the first goto. Chrome ignores credentials written into the flag. CrawlRelay residential proxies read the country and the sticky session from the username, so each page or context can have its own exit. Traffic costs $0.50/GB from 50 GB. Paid in crypto, with no KYC.
Puppeteer setup at a glance
| Proxy address | --proxy-server=http://gw.crawlrelay.com:7777 in args, or proxyServer on a browser context |
|---|---|
| Credentials | await page.authenticate({ username, password }) before navigating |
| Separate sessions | browser.createBrowserContext({ proxyServer }): one context per session |
| SOCKS5 | Not with authentication in Chrome: use the HTTP port |
| Saving traffic | page.setRequestInterception(true) and abort images, fonts and media |
How do I set up a single authenticated proxy?
This is the minimal pattern. The try … finally block closes Chrome even when a navigation fails, which matters on servers where orphan browsers pile up.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch({
args: ["--proxy-server=http://gw.crawlrelay.com:7777"],
});
try {
const page = await browser.newPage();
await page.authenticate({ username: "USERNAME-country-us", password: "PASSWORD" });
await page.setRequestInterception(true);
page.on("request", (req) =>
["image", "font", "media"].includes(req.resourceType()) ? req.abort() : req.continue()
);
const res = await page.goto("https://example.com/", { waitUntil: "domcontentloaded", timeout: 60000 });
console.log(res.status(), await page.title());
} finally {
await browser.close();
}How do I run several sessions or countries in one browser?
Create one browser context per session. A context is an isolated profile with its own cookies, cache and network state, so the credentials you give one context do not leak into another. Give each context a username with its own country or session ID.
import puppeteer from "puppeteer";
import { randomUUID } from "node:crypto";
const GATEWAY = "http://gw.crawlrelay.com:7777";
const browser = await puppeteer.launch();
async function visit(country, url) {
const session = randomUUID().replaceAll("-", "").slice(0, 10);
const context = await browser.createBrowserContext({ proxyServer: GATEWAY });
try {
const page = await context.newPage();
await page.authenticate({
username: `USERNAME-country-${country}-session-${session}-ttl-20`,
password: "PASSWORD",
});
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });
return { country, session, ip: await page.evaluate(() => document.body.innerText.trim()) };
} finally {
await context.close();
}
}
try {
const results = await Promise.all([
visit("us", "https://api.ipify.org"),
visit("gb", "https://api.ipify.org"),
]);
console.table(results);
} finally {
await browser.close();
}Why does Puppeteer show a 407 or ERR_TUNNEL_CONNECTION_FAILED?
Because Chrome sent no credentials, or the wrong ones. Check three things:
page.authenticate()runs before the firstgotoon that page.- The
--proxy-serverflag orproxyServerholds only the scheme, host and port, with nouser:pass@part. - The username has no typo in its targeting part: test it with curl first, as shown on the 407 page.
If curl succeeds with the same username and password and Puppeteer still fails, read ERR_TUNNEL_CONNECTION_FAILED.
How much traffic does a Puppeteer page cost?
Everything Chrome downloads is billed: the HTML, scripts, styles, images, fonts and the API calls the page makes. The HTTP Archive Web Almanac 2025 measured a median page weight of 2,164 KB on mobile and 2,412 KB on desktop. Blocking images, fonts and media with request interception usually cuts that to a few hundred kilobytes, and returning cached responses for repeated scripts cuts it further. Use rotating mobile IPs only for the pages that need them: mobile traffic costs $1.50/GB from 5 GB, residential $0.50/GB from 50 GB.
Puppeteer and proxies: questions
Can I put the username and password in --proxy-server?
No. Chrome ignores credentials in the --proxy-server flag, and the first HTTPS request then fails with a 407 hidden behind ERR_TUNNEL_CONNECTION_FAILED. Put only http://host:port in the flag and pass the credentials with page.authenticate() on every new page.
How do I rotate the IP between pages in Puppeteer?
Open each page in a new browser context, or give each page a username without a session ID. A new context opens new connections, and each new connection usually gets a different residential exit. Within one page, Chrome reuses connections, so requests to the same site often share an exit until the connection closes.
Does page.authenticate() work with request interception?
Yes. Puppeteer handles the proxy's authentication challenge itself, and your interception handler only needs to call continue() or abort() on each request. Set up authenticate() first, then turn interception on, as in the first example.
Should I use puppeteer-extra stealth plugins with proxies?
We do not document or support tools meant to hide automation or defeat bot detection. Their purpose is to get past a site's decision to filter automated visitors, which our Acceptable Use Policy does not allow. Render public pages at a polite pace and use official APIs where sites provide them.
Which Chrome does Puppeteer use with the proxy?
Recent Puppeteer versions download a matching Chrome for Testing build and use it by default. The proxy flags and page.authenticate() work the same way in it as in regular Chrome. If you point Puppeteer at your own Chrome with executablePath, keep that browser up to date.
Run Puppeteer through residential IPs
$5.94 for 10 GB, $0.50/GB from 50 GB.