-
Install Puppeteer
Installing the package also downloads a matching build of Chrome for Testing. Save the examples as
.mjsfiles to useimportand top-levelawait.Shell npm install puppeteer -
Launch Chrome through the gateway
Chrome ignores credentials inside
--proxy-server, so pass only the host and port there and supply the username and password withpage.authenticate()before the first navigation.JavaScript // launch.mjs import puppeteer from 'puppeteer'; const browser = await puppeteer.launch({ args: ['--proxy-server=http://gw.proxonym.com:8000'], }); const page = await browser.newPage(); await page.authenticate({ username: 'USERNAME', password: 'PASSWORD' }); await page.goto('https://api.ipify.org'); console.log('Exit IP:', await page.evaluate(() => document.body.innerText)); await browser.close(); -
Keep one IP for the whole session
A page opens many connections, and with a rotating username each one can exit from a different IP. Add a
-session-ID so every connection shares one IP, and set-lifetime-longer than the job, up to 120 minutes.JavaScript import { randomBytes } from 'node:crypto'; const session = randomBytes(4).toString('hex'); await page.authenticate({ username: `USERNAME-country-us-session-${session}-lifetime-30`, password: 'PASSWORD', }); -
Run one IP per browser context
Browser contexts keep separate cookies and connections, and each can use its own proxy. Give every context a different session ID or country to run several identities from one Chrome process.
JavaScript // contexts.mjs import { randomBytes } from 'node:crypto'; import puppeteer from 'puppeteer'; const browser = await puppeteer.launch(); async function openIdentity(country) { const context = await browser.createBrowserContext({ proxyServer: 'http://gw.proxonym.com:8000', }); const page = await context.newPage(); await page.authenticate({ username: `USERNAME-country-${country}-session-${randomBytes(4).toString('hex')}-lifetime-30`, password: 'PASSWORD', }); return page; } const pages = await Promise.all(['us', 'gb', 'de'].map(openIdentity)); for (const page of pages) { await page.goto('https://api.ipify.org'); console.log(await page.evaluate(() => document.body.innerText)); } await browser.close(); -
Match the browser to the location
Target a country, state or city in the username, then align the timezone and language with it so the browser and the IP tell the same story.
JavaScript await page.authenticate({ username: 'USERNAME-country-de-city-berlin-session-b3x9-lifetime-30', password: 'PASSWORD', }); await page.emulateTimezone('Europe/Berlin'); await page.setExtraHTTPHeaders({ 'Accept-Language': 'de-DE,de;q=0.9' }); await page.goto('https://ipinfo.io/json'); console.log(await page.evaluate(() => document.body.innerText)); -
Save traffic and handle errors
Residential traffic is metered per GB, so block images, media and fonts you do not need. A refused tunnel makes
page.goto()thrownet::ERR_TUNNEL_CONNECTION_FAILED, which is worth a retry with a new session ID, while a rejected login comes back as a 407 status.JavaScript await page.setRequestInterception(true); page.on('request', (request) => { if (['image', 'media', 'font'].includes(request.resourceType())) request.abort(); else request.continue(); }); try { const response = await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 60_000, }); console.log('Status:', response?.status()); } catch (err) { console.error('Navigation failed:', err.message); }
Frequently asked questions
Why does Puppeteer fail with ERR_TUNNEL_CONNECTION_FAILED?
The gateway refused to open a tunnel to the target. A 402 means your balance is empty or the plan has expired, and a 403 means the target is blocked under the acceptable use policy, while 502 and 504 are transient, so retry with a new session ID. Login problems look different: a 407 page status, or ERR_INVALID_AUTH_CREDENTIALS when page.authenticate() was not called.
Can each page or tab use a different IP?
Yes. Create a browser context per identity with its own proxyServer, open pages inside it, and authenticate each page with a username that carries a different session ID or country. Pages in the same context share cookies and connections, so treat one context as one identity and one sticky IP.
Does headless mode change how the proxy works?
No. The proxy flag, the authentication step and the username parameters are identical in headless and headed mode. What differs is the browser fingerprint, so align the timezone, language and viewport with the exit location, and keep request rates realistic for the site you visit.
How can I reduce traffic per page?
Block images, media and fonts with request interception, wait for domcontentloaded instead of full network idle, and reuse one sticky session for a whole flow instead of reloading pages. A full page load can transfer several megabytes, so these steps make a visible difference on a per-GB residential plan.
