
Before you start
An active Crawlhub proxy and its assigned host, port, username and password. PROXY_SERVER includes the protocol and port, but no username or password. Use the values from your account; these examples do not activate a proxy.
Node.js 22.12 or newer, the browser library and proxy-chain. This Chromium recipe uses a loopback-only bridge for authenticated HTTP(S) upstream proxies, so proxy credentials are not handed to website authentication challenges.
Where the proxy fits in Puppeteer
Puppeteer automates a browser from Node.js. This guide routes a browser context through a local bridge that authenticates to your assigned upstream proxy.
- Context-level routing
- Use createBrowserContext with proxyServer so the routing choice belongs to that context.
- Proxy-only credentials
- The local bridge handles upstream login, keeping proxy credentials out of the browser’s website authentication handler.
- Real browser output
- Read the rendered DOM, interact with controls or capture the page using the same browser session.
- Predictable cleanup
- Close contexts and the browser when the job finishes or throws an error.
1. Install Puppeteer
Start a Node.js project and install Puppeteer. The full package manages its supported browser download; puppeteer-core requires you to supply a browser separately.
npm init -y
npm install puppeteer proxy-chain2. Set the proxy environment
Keep the host and port in PROXY_SERVER and keep the login fields separate. Replace the example tokens with your assigned account values.
The example starts a loopback-only proxy bridge automatically. The bridge connects to the assigned upstream proxy; the browser receives only the local URL.
export PROXY_SERVER='http://YOUR_PROXY_HOST:YOUR_PORT'
export PROXY_USERNAME='YOUR_PROXY_USERNAME'
export PROXY_PASSWORD='YOUR_PROXY_PASSWORD'
export TARGET_URL='https://example.com/'3. Create the context and fetch a page
Download both files below into the same project folder, then run puppeteer.mjs. The shared helper handles upstream authentication; the browser loads through its local endpoint.
The example checks the response status, prints the title and closes both the browser and bridge on completion or failure.
node puppeteer.mjsimport { Server } from 'proxy-chain';
export async function createProxyBridge() {
const { PROXY_SERVER, PROXY_USERNAME, PROXY_PASSWORD } = process.env;
if (!PROXY_SERVER || !PROXY_USERNAME || !PROXY_PASSWORD) {
throw new Error('Set PROXY_SERVER, PROXY_USERNAME and PROXY_PASSWORD.');
}
const upstream = new URL(PROXY_SERVER);
if (!['http:', 'https:'].includes(upstream.protocol) ||
upstream.username || upstream.password || upstream.search ||
upstream.hash || (upstream.pathname && upstream.pathname !== '/')) {
throw new Error('Use an HTTP(S) proxy server URL without credentials, a path or a query.');
}
upstream.username = PROXY_USERNAME;
upstream.password = PROXY_PASSWORD;
const server = new Server({
host: '127.0.0.1',
port: 0,
prepareRequestFunction: () => ({ upstreamProxyUrl: upstream.href }),
});
await server.listen();
return {
url: `http://127.0.0.1:${server.port}`,
close: () => server.close(true),
};
}import puppeteer from 'puppeteer';
import { createProxyBridge } from './proxy_bridge.mjs';
const bridge = await createProxyBridge();
let browser;
try {
browser = await puppeteer.launch({ headless: true });
const context = await browser.createBrowserContext({ proxyServer: bridge.url });
const page = await context.newPage();
const response = await page.goto(process.env.TARGET_URL || 'https://example.com/', {
waitUntil: 'domcontentloaded', timeout: 30000,
});
if (!response || !response.ok()) {
throw new Error(`Page returned HTTP ${response?.status() ?? 'unknown'}.`);
}
console.log(JSON.stringify({ status: response.status(), title: await page.title() }));
await context.close();
} finally {
try { await browser?.close(); } finally { await bridge.close(); }
}4. Check the IP and retain session state
A successful request confirms that the page loaded, but it does not by itself prove the exit IP. Run the same code against an IP-check endpoint you trust, inspect the returned address, then switch to your target. Keep the requested location and session settings consistent when comparing results.
Use one context for a related sequence of pages. Use a new context when you need separate cookies. Request a different proxy session explicitly when your service requires one.
Troubleshooting
Proxy authentication fails
Check the three proxy environment variables. Do not put the proxy password into page.authenticate; that handler can also answer destination-site challenges.
Requests use the wrong route
Create the page from the proxied context, not from an unrelated default context.
Target blocks a request
Inspect the response status and contents. A proxy connection alone does not guarantee that a website will accept a browser.
