Proxies for browser automation
Run Playwright, Puppeteer and Selenium through netdash with one sticky session per browser context, credentials passed the way each tool expects, and only the traffic you need.
Proxy URL, exit IP in Germany
http://USERNAME-cc-DE-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000- scale-eu.netdash.io:2000
- Host and port pick the plan and the gateway.
- USERNAME
- Your account username from the dashboard. One pair works on every residential and mobile plan.
- -cc-DE
- The exit country, as a two-letter ISO code. Use GB for the United Kingdom.
- -session-a1b2c3d4-ttl-30
- Keeps one exit IP for 30 min while that IP stays available.
- Server and credentials as separate fields
- Sticky sessions per context
- No per-account concurrency limit
Where browser fleets go wrong with a proxy
A browser behind a proxy fails in a few predictable ways, and most of them have nothing to do with the website. The proxy gets the credentials in the wrong place, one exit has to serve tests that need several, or the browser spends much of its traffic on files no test looks at.
Each of these has a fix in how you configure the browser, which the rest of this page walks through.
- Credentials in the proxy URL: Chromium-based browsers don’t read a username and password from the proxy server URL, so the gateway answers
407 Proxy Authentication Required - One proxy for the whole browser when a test suite needs several countries or sessions
- The exit IP changing mid-test while the cookies stay, so the site sees one session arrive from two addresses
- GB spent on images, video and fonts that no test inspects
Why each browser context needs its own exit
CI runners and containers usually sit in one or two cloud regions. Tests and workflows that depend on the visitor’s country, such as language, currency or consent banners, need an exit in that country, and when one run covers several countries, each browser context needs its own.
A context’s exit IP should also stay stable for as long as its cookies matter. A sticky session with a TTL longer than the job keeps the two together; a new context gets a new session ID and starts clean. For country-by-country checks of your own site, see localization testing.
From one browser to many contexts
Step 1: Choose the proxy scope
Per browser when every page in the run shares one exit; per context when one browser process needs several countries or sessions.
Step 2: Map sessions to contexts
One session ID per context or worker, such as
Sessions and TTLsw3k9x2m1: the worker index plus a short run hash, letters and digits only. Pick a TTL longer than the job,30or720.Step 3: Pass credentials as fields
Server
Proxy string formathttp://scale-eu.netdash.io:2000, with the username and password in their own fields, never inside the server URL.Step 4: Cut the traffic
Abort image, media and font requests unless the test needs them, and send hosts you operate directly.
Step 5: Confirm the exit
Open
https://api.ipify.org?format=jsonin each context and log the IP with the worker ID.Step 6: Handle failures by type
Retry network errors with a new session ID; stop when the site limits you. The table further down maps symptoms to causes.
How each tool takes a proxy
Where the proxy and its credentials go in each tool, and whether one process can use several exits.
| Attribute | Where the proxy is set | How credentials are passed | Separate exits in one process | Guide |
|---|---|---|---|---|
| Playwright | chromium.launch({ proxy }) or browser.newContext({ proxy }) | username and password fields next to server | Yes, one per browser context | Playwright guide |
| Puppeteer | --proxy-server launch argument, or proxyServer in browser.createBrowserContext() | page.authenticate({ username, password }) on each page | Yes, one per browser context | Puppeteer guide |
| Selenium | The proxy setting in the browser options when the driver session starts | Chrome doesn’t read them from the proxy URL; the guide shows how to supply them | One per driver session | Selenium guide |
| Cloud browsers | The provider’s own proxy setting | Depends on the provider | Depends on the provider | Cloud browser guide |
Based on each tool’s official documentation, checked on 2026-10-09.
Session layout for a parallel run
A runner in Europe collecting German pages. The examples use the Scale plan; every residential plan uses the same syntax, and you switch plans by changing the host and port.
- Plan and endpoint
Scale:
scale-eu.netdash.io:2000The Europe gateway, for exits in Europe such as Germany.
- Exit country
Per context:
-cc-DEin the exampleEach context in one browser can use a different country. Targeting is country-level.
- Session mode and TTL
Sticky,
-ttl-30per context;720(12 h) for long jobs in one contextThe exit IP stays the same for the TTL while that IP stays available. Pick a TTL that covers the time the context’s cookies are in use.
- Session ID
Worker index plus a short run hash, for example
w3k9x2m1Unique per context. Letters and digits only, no hyphens.
- Parallelism
One context per worker; add workers as needed
There is no per-account concurrency limit. Keep each site’s request rate reasonable.
- Traffic
Abort
image,mediaandfontrequestsFewer GB per page. Keep them when a test checks visuals.
- Hosts you operate
bypassin Playwright,proxyBypassListin PuppeteerStaging servers and asset hosts you run don’t need an exit IP.
Build the connection for one context
Pick the plan, gateway, country and TTL, then copy the server and username into the context’s proxy fields.
Your route
Through the gateway to an exit IP in , , on the plan.
Proxy URL
http://USERNAME-cc-DE-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000Replace USERNAME and PASSWORD with the credentials from your dashboard.
Scale, Europe gateway, exit IP in Germany, sticky for 30 min.
curl --max-time 30 -x "http://USERNAME-cc-DE-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000" \
"https://api.ipify.org?format=json"import requests
proxy = "http://USERNAME-cc-DE-session-a1b2c3d4-ttl-30:PASSWORD@scale-eu.netdash.io:2000"
proxies = {"http": proxy, "https": proxy} # set both, or HTTPS requests bypass the proxy
response = requests.get("https://api.ipify.org?format=json", proxies=proxies, timeout=30)
response.raise_for_status()
print(response.text)import { ProxyAgent, fetch } from "undici";
const dispatcher = new ProxyAgent({
uri: "http://scale-eu.netdash.io:2000",
token: "Basic " + Buffer.from("USERNAME-cc-DE-session-a1b2c3d4-ttl-30:PASSWORD").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher, signal: AbortSignal.timeout(30_000) });
console.log(await res.text());import { chromium } from "playwright";
const browser = await chromium.launch({
proxy: {
server: "http://scale-eu.netdash.io:2000",
username: "USERNAME-cc-DE-session-a1b2c3d4-ttl-30",
password: "PASSWORD",
},
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.textContent("body"));
await browser.close();Example output
{"ip":"203.0.113.10"}Your output shows a different address in the country you chose.
Per-context sessions with assets skipped
Both examples give each browser context its own sticky session on the Europe gateway, skip images, media and fonts, and send staging.example.com directly. Playwright takes the credentials in the context’s proxy settings; Puppeteer sets the proxy on the context and the credentials on each page.
Swap the test URL for your own pages, and keep the session ID unique per context so parallel workers never share a session.
- The server URL carries no credentials, so Chromium never has to read them from it
- Puppeteer stalls intercepted requests until they are continued or aborted, so the handler always does one or the other
- Navigation timeouts of 30 seconds
- Syntax-checked; the API calls follow the current Playwright and Puppeteer documentation
import { chromium } from "playwright";
const SKIP = new Set(["image", "media", "font"]);
const RUN = Date.now().toString(36).slice(-6); // short run hash: letters and digits
// One context per worker: its own sticky session, exit IP and cookies.
async function contextFor(browser, worker, country = "DE") {
const context = await browser.newContext({
proxy: {
server: "http://scale-eu.netdash.io:2000",
username: `${process.env.NETDASH_USERNAME}-cc-${country}-session-w${worker}${RUN}-ttl-30`,
password: process.env.NETDASH_PASSWORD,
bypass: "staging.example.com", // hosts you operate don't need an exit IP
},
});
await context.route("**/*", (route) =>
SKIP.has(route.request().resourceType()) ? route.abort() : route.continue(),
);
return context;
}
const browser = await chromium.launch();
await Promise.all([0, 1, 2].map(async (worker) => {
const context = await contextFor(browser, worker);
const page = await context.newPage();
await page.goto("https://api.ipify.org?format=json", { timeout: 30_000 });
console.log(`worker ${worker}:`, await page.textContent("body"));
await context.close();
}));
await browser.close();import puppeteer from "puppeteer";
const SKIP = new Set(["image", "media", "font"]);
// One browser context per session; credentials go to page.authenticate().
async function pageFor(browser, sid, country = "DE") {
const context = await browser.createBrowserContext({
proxyServer: "http://scale-eu.netdash.io:2000",
proxyBypassList: ["staging.example.com"],
});
const page = await context.newPage();
await page.authenticate({
username: `${process.env.NETDASH_USERNAME}-cc-${country}-session-${sid}-ttl-30`,
password: process.env.NETDASH_PASSWORD,
});
await page.setRequestInterception(true);
page.on("request", (request) => {
if (request.isInterceptResolutionHandled()) return;
if (SKIP.has(request.resourceType())) request.abort();
else request.continue();
});
page.setDefaultNavigationTimeout(30_000);
return page;
}
const browser = await puppeteer.launch();
const page = await pageFor(browser, "w0k9x2m1");
await page.goto("https://api.ipify.org?format=json");
console.log(await page.$eval("body", (el) => el.textContent));
await browser.close();Spend GB on pages, not assets
You pay for the traffic that goes through the proxy, with no hidden multiplier. A browser downloads every image, font and video on a page, which often outweighs the HTML and data your test or workflow actually reads.
Three habits keep GB per page down. Measure a typical page in your own run, then price the total in the price calculator.
- Abort resource types you don’t need:
image,media,font - Send hosts you operate directly: list them in the tool’s setting for hosts that skip the proxy
- Reuse a context for pages that share a session instead of opening a new one per page
Browser automation proxies: which product for which job
Residential suits most browser fleets. Static ISP and datacenter IPs are available through sales while self-serve checkout is paused.
Residential proxies
Self-serveA country per context, sticky sessions sized to the job, and GB billing that suits bursty test runs.
From $1.20/GB, graduated per order
ISP proxies
Through salesLong-lived browser sessions that should keep one static IP across days.
$1.60 per IP per 30 days
Datacenter proxies
Through salesSteady browser jobs against systems you run, such as your staging or your own apps, where billing per IP suits the load.
$1.40 per IP per 30 days
When a run fails
| Attribute | Likely cause | What to do |
|---|---|---|
| 407 Proxy Authentication Required | Wrong credentials, or the username syntax is off | Check the username and password fields; see troubleshooting |
| Requests leave without the proxy | Proxy set on the wrong object, or the host is listed to skip the proxy | Log the exit IP per context; see troubleshooting |
| Timeout or connection reset | A network problem between the browser, the gateway and the site | Retry with backoff and a new session ID |
| A CAPTCHA, a login wall or a 429 response | The site is limiting you | Slow down or stop. Don’t switch sessions to continue |
Automate responsibly
Automate browsers against your own applications, or against public pages within each site’s terms and robots.txt. Keep request rates reasonable and stop when a site shows a CAPTCHA, a login wall or a rate limit: netdash doesn’t solve challenges, and switching sessions to continue is not how it’s meant to be used. More in how we expect netdash to be used.
Guides for each browser tool
- Playwright
Launch and per-context proxies, with credentials as separate fields.
- Puppeteer
Proxy launch arguments, browser contexts and page authentication.
- Selenium
Proxy options per driver session and how to pass credentials.
- Cloud browsers
Remote and hosted browsers, and where the proxy layer sits.
- Proxies for AI agents
Exit countries and sticky sessions for agents that drive a browser.
- Rotating and sticky sessions
Session IDs, TTLs and when an exit IP changes.
- Proxy string format
Hosts, ports and the username syntax for every plan.
- Troubleshooting
Fix `407` errors, timeouts and requests that skip the proxy.
- Static vs rotating proxies
When a fixed IP beats a rotating one, and the other way round.
Browser automation questions
Chromium-based browsers don’t read credentials from the proxy server URL. Pass them separately: Playwright’s username and password fields, Puppeteer’s page.authenticate(), and the method in the Selenium guide.
Per context when one browser process needs different countries or sessions; Playwright and Puppeteer both support it. Give each context its own session ID.
No. Your browser renders the page; netdash forwards its traffic unchanged.
Skip images, media and fonts you don’t need, send hosts you operate directly, and reuse contexts. You pay for the traffic that goes through the proxy; the calculator turns an estimate into a price.
ISP and datacenter static IPs exist, but self-serve checkout for them is paused. Contact sales with the locations and number of IPs you need.
Put your first context behind a proxy
Start with one context and one session ID, confirm its exit IP, then add workers. The Playwright guide covers launch-level and per-context proxies.