How to Use a Proxy for Each Page in Puppeteer
Route Puppeteer pages through different proxies with BrowserContexts when your version and protocol support it, or use separate browser processes.
To use a different proxy for each Puppeteer page, put each page in a separate BrowserContext and set that context’s proxyServer option, if your installed Puppeteer version and browser protocol support it. This option appears in Puppeteer’s Next API reference; the 25.12.0 changelog records support for incognito contexts with BiDi, so confirm compatibility in your installed release. If context-level routing is unavailable or does not work in your setup, launch a separate browser process for each proxy. A --proxy-server launch argument applies to the browser process, not to one page. There is no general Page-level proxy setter established in the referenced API docs.
1. Choose the routing scope
| Approach | Scope | Use it when | Trade-off |
|---|---|---|---|
Context proxyServer |
One BrowserContext and its pages | Your installed version and protocol support it | Confirm compatibility before relying on it |
Launch --proxy-server |
One browser process | You need the broadly documented launch-argument route | Different proxy routes need different browser processes |
BrowserContexts isolate cookies and cache, and pages created by a context belong to it. Popups opened by a page remain in that page’s parent context. Context isolation can therefore help keep both routing and website state grouped, but it is not a mechanism for assigning a proxy to an individual tab inside the same context. See the BrowserContext API, Next BrowserContextOptions, and createBrowserContext API.
2. Route pages with BrowserContexts
This example creates two contexts, each with its own proxy, and puts one page in each. It uses the API documented in Puppeteer’s Next reference. Check your installed Puppeteer type definitions and release documentation first; do not assume that every stable version or protocol accepts this option. The Puppeteer 25.12.0 changelog specifically mentions proxy support for incognito contexts under BiDi.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const contextA = await browser.createBrowserContext({
proxyServer: 'http://proxy-a.example:8080',
});
const contextB = await browser.createBrowserContext({
proxyServer: 'http://proxy-b.example:8080',
});
try {
const pageA = await contextA.newPage();
const pageB = await contextB.newPage();
await Promise.all([
pageA.goto('https://example.com', { waitUntil: 'domcontentloaded' }),
pageB.goto('https://example.com', { waitUntil: 'domcontentloaded' }),
]);
console.log('Both pages navigated through their context routes.');
} finally {
await Promise.all([contextA.close(), contextB.close()]);
}
} finally {
await browser.close();
}
Replace the example proxy hosts and ports with endpoints supplied by your proxy operator. The code uses placeholder hostnames and will not connect as written. Create each page through its intended context; a page created in the default context does not inherit another context’s route.
Verify the route you actually got
Before sending real work, navigate a page through each context to an IP-check endpoint you control or trust and compare the reported egress address with the expected proxy. This catches unsupported context options, bad credentials, and proxy-side routing issues. A successful page navigation alone does not prove the proxy was used.
3. Use separate browser processes as a fallback
When your installed API or protocol does not support context-level routing, give each browser process its own launch argument. Puppeteer documents args as additional command-line arguments for the browser.
import puppeteer from 'puppeteer';
async function captureThrough(proxyServer, url) {
const browser = await puppeteer.launch({
args: [`--proxy-server=${proxyServer}`],
});
try {
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'domcontentloaded' });
return await page.title();
} finally {
await browser.close();
}
}
const [titleA, titleB] = await Promise.all([
captureThrough('http://proxy-a.example:8080', 'https://example.com'),
captureThrough('http://proxy-b.example:8080', 'https://example.com'),
]);
console.log({ titleA, titleB });
For a worker that handles multiple jobs per proxy, keep a browser process associated with that proxy and create its pages there. Close pages and browsers when work ends, and limit concurrency to what your machine and proxy service can sustain. Separate processes cost more memory and startup work than reusing one browser; no universal performance ratio applies.
The launch option is browser-scoped. Opening another page in the same process does not give that page a different launch proxy. Puppeteer’s LaunchOptions reference documents args.
4. Authenticate to an HTTP proxy
For HTTP authentication, Puppeteer documents page.authenticate({ username, password }). Set credentials on the page before navigation:
const page = await context.newPage();
await page.authenticate({
username: process.env.PROXY_USERNAME,
password: process.env.PROXY_PASSWORD,
});
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
Use the authentication method your proxy supports. This API is documented for HTTP authentication; it is not a universal solution for every proxy authentication scheme. Keep secrets in environment variables or a secrets manager, and avoid logging credential-bearing proxy URLs. Puppeteer notes that page.authenticate() turns on request interception behind the scenes and this might affect performance. See Page.authenticate().
5. Version and protocol compatibility
The cited stable Puppeteer API references are labeled 25.12.0. The explicit proxyServer and proxyBypassList context options are documented in the Next API reference, while the 25.12.0 changelog notes proxy support for incognito BrowserContexts under BiDi. These are distinct pieces of evidence; the Next reference alone does not establish support in every stable release and protocol.
- Check your installed package version and its matching API documentation or type definitions.
- Check whether your connection uses the protocol expected by the feature, especially if using BiDi.
- Run a small route verification before deploying context routing.
- If the option is missing or the request exits through the wrong route, use one browser process per proxy.
- Keep Puppeteer and browser versions aligned with the supported browser guidance, especially when using a non-default browser.
6. Bypass rules and proxy behavior
Puppeteer’s Next BrowserContextOptions reference also lists proxyBypassList, an array of hosts to bypass the proxy. Confirm that this option exists in your installed release before using it. A bypass rule means some requests may leave through the direct network route, so keep the list deliberate and verify the resulting egress behavior.
Proxy routing can also depend on the proxy server itself: endpoint availability, allowed destinations, authentication policy, DNS behavior, and outbound filtering are controlled by that service. Puppeteer configuration cannot correct a proxy-side denial or outage.
7. Performance, reliability, and cost considerations
- Contexts: Reusing one browser process can avoid repeatedly starting browsers. Contexts group pages and isolate cookies/cache. The context proxy option’s availability depends on version and protocol.
- Separate processes: This is the documented fallback for launch-level routing, with additional browser process startup and resource use. Reuse each process for multiple jobs using its proxy when appropriate.
- Authentication:
page.authenticate()enables request interception internally, which might affect performance. Avoid adding additional interception handlers unless needed; if you enable interception yourself, every request must be continued, responded to, or aborted. See Puppeteer request interception. - Reliability: Treat proxy errors as job failures with bounded retries and timeouts. A retry through the same unhealthy endpoint may fail again; record which route was assigned without recording secrets.
- Cost: Browser processes consume compute and memory, while proxy providers may charge according to their own plans and traffic rules. This research does not establish provider prices or comparative performance figures, so check the terms for the proxy service you use.
8. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Both pages appear to use the same IP | Both pages share a browser-level route, or context routing is unsupported/ignored | Ensure pages are created in separate configured contexts; verify installed API and protocol; use separate processes if needed. |
proxyServer is rejected or missing in types |
The installed version does not expose the Next API option | Consult documentation and type definitions for that exact version. Use launch arguments with separate browser processes. |
| Navigation fails or times out | Proxy endpoint unavailable, destination blocked, credentials rejected, or network slow | Check endpoint, port, authentication scheme, destination policy, and proxy health. Retry with a bounded timeout strategy. |
| Authentication prompt or 407 response | Credentials are missing/wrong or the proxy uses an unsupported authentication scheme | Confirm the proxy’s required scheme. For HTTP authentication, call page.authenticate() before navigation. |
| Proxy works but some requests bypass it | A bypass list or proxy-side routing rule applies | Review proxyBypassList and proxy policy; verify egress for the relevant host. |
| Slower requests after adding authentication | page.authenticate() enables request interception behind the scenes |
Measure the workload and keep authentication setup scoped to pages that need it. |
| Unexpected behavior with a custom Chrome binary | Puppeteer/browser version mismatch | Use the browser version paired with your Puppeteer release where possible, and review supported-browser guidance. |
9. Or skip the browser setup
If your task is to capture a website screenshot rather than control arbitrary browser traffic, ScreenshotNeo provides a screenshot API and MCP server. One GET request returns an image or PDF; the call below saves a WebP screenshot. Read the ScreenshotNeo API documentation for available parameters.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free for 1,000 screenshots a month, with no card.
10. Frequently asked questions
Can I change a page’s proxy after creating it?
The documented approaches configure routing at context creation or browser launch. Plan the route before creating or navigating the page; the cited APIs do not establish a general Page-level proxy setter.
Do popups inherit the page’s proxy?
Popups belong to their parent page’s BrowserContext. That keeps them associated with the context, but the actual proxy behavior still depends on context-routing support in your version and protocol.
Should every URL use a separate browser process?
No. Use separate processes when distinct launch-level proxy routes are required and context routing is unavailable. A process can handle multiple pages that share its route.
Does Puppeteer choose or provide proxy servers?
The APIs configure a proxy endpoint you supply. The cited Puppeteer references do not provide proxy endpoints or verify a proxy provider.


