The essential points from this guide -- each one is explained in detail below.
Pass --proxy-server=host:port in Puppeteer launch args for unauthenticated proxy routing.
The proxy-chain package creates a local proxy that forwards to an authenticated upstream proxy.
page.authenticate() handles basic auth but only works for site authentication, not proxy authentication in all cases.
puppeteer-extra with the stealth plugin reduces bot detection when combined with residential proxies.
The --proxy-server Chrome flag routes all Puppeteer traffic through the specified proxy. This is the simplest configuration and works with any unauthenticated proxy. Pass it as part of the args array in the launch options.
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({
args: ['--proxy-server=gw.knoxproxy.com:7000'],
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
const body = await page.evaluate(() => document.body.textContent);
console.log(body);
await browser.close();This approach has the same limitation as Selenium: Chrome's --proxy-server flag does not support username:password authentication in the URL. For authenticated proxies, you need the proxy-chain package or a Chrome extension approach similar to the one described in the Selenium guide.
The proxy-chain package from Apify is the standard solution for authenticated proxies in Puppeteer. It starts a local proxy server that accepts unauthenticated connections and forwards them to your authenticated upstream proxy, handling the 407 challenge internally.
const puppeteer = require('puppeteer');
const proxyChain = require('proxy-chain');
const oldProxyUrl = 'http://USER:PASS@gw.knoxproxy.com:7000';
const newProxyUrl = await proxyChain.anonymizeProxy(oldProxyUrl);
// newProxyUrl is something like http://127.0.0.1:45678
const browser = await puppeteer.launch({
args: [`--proxy-server=${newProxyUrl}`],
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
console.log(await page.evaluate(() => document.body.textContent));
await browser.close();
await proxyChain.closeAnonymizedProxy(newProxyUrl, true);The anonymizeProxy function returns a local URL that you pass to Chrome. When you are done, call closeAnonymizedProxy to clean up the local server. This approach works with any proxy provider that uses username:password authentication, including KnoxProxy's backconnect gateway.
puppeteer-extra with the stealth plugin patches common browser fingerprinting vectors that anti-bot systems check. Combined with residential proxies, this significantly reduces detection rates compared to vanilla Puppeteer.
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const proxyChain = require('proxy-chain');
puppeteer.use(StealthPlugin());
const proxyUrl = await proxyChain.anonymizeProxy(
'http://USER:PASS@gw.knoxproxy.com:7000'
);
const browser = await puppeteer.launch({
args: [
`--proxy-server=${proxyUrl}`,
'--no-sandbox',
'--disable-setuid-sandbox',
],
headless: 'new',
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });The stealth plugin patches navigator.webdriver, Chrome runtime, and other properties that reveal automation. It is not a silver bullet -- sophisticated anti-bot systems use deeper fingerprinting -- but it raises the bar significantly. Pair it with realistic browsing patterns like random delays and scroll behavior for best results.
For scraping that requires multiple simultaneous proxy identities, launch separate browser instances or use proxy-chain to create multiple anonymized proxy endpoints. Each browser instance maintains its own proxy connection, cookies, and session state.
async function createProxiedBrowser(proxyUrl) {
const localProxy = await proxyChain.anonymizeProxy(proxyUrl);
const browser = await puppeteer.launch({
args: [`--proxy-server=${localProxy}`],
});
return { browser, localProxy };
}
// Launch 3 browsers with different geo-targeted proxies
const proxies = [
'http://USER-country-us:PASS@gw.knoxproxy.com:7000',
'http://USER-country-gb:PASS@gw.knoxproxy.com:7000',
'http://USER-country-de:PASS@gw.knoxproxy.com:7000',
];
const sessions = await Promise.all(proxies.map(createProxiedBrowser));Each browser instance consumes significant memory (150-300 MB), so limit concurrency based on available system resources. Close browsers and proxy chains promptly after each task completes. For high-throughput scraping, consider Playwright's per-context proxy model which is more memory-efficient than launching separate browser processes.
Ready to put this into practice? Browse Residential Proxies
KnoxProxy Research Team · Technical Content
Network engineers and proxy infrastructure specialists with 10+ years in anti-bot systems, web scraping, and IP routing.
90.4M+ ethically sourced residential IPs across 195 countries. Start free -- no credit card required.