Playwright proxy setup and authentication
Pass proxy: { server, username, password } when you launch the browser or create a context. Put the netdash username, with its country and session parameters, in username, not in the server URL.
- Updated
- Reading time
- 6 min
- Checked against
- 7 sources
A Playwright proxy is a launch option or a context option, in the Node.js and Python APIs alike, and both forms accept a username and password. That is all netdash needs: no SDK, no extension, no local helper proxy. This page is one of the proxy integration guides and covers the Playwright library itself.
netdash is an HTTP proxy. HTTPS sites go through a CONNECT tunnel, so the server value always starts with http://, even when every page you open uses HTTPS.
Quick start (Node.js)
With Playwright and its Chromium build installed, this script opens a test page through the Americas gateway on the Scale plan and prints the exit IP:
import { chromium } from "playwright";
const browser = await chromium.launch({
proxy: {
server: "http://scale-us.netdash.io:2000",
username: "USERNAME-cc-US",
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.
Replace USERNAME and PASSWORD with the credentials from the dashboard (Residential → Generate), or read them from the environment as the examples below do. The output is JSON with the exit IP, for example {"ip":"203.0.113.10"}. No account yet? The quickstart goes from sign-up to a first proxied request.
Playwright proxy authentication
The proxy option has four fields, and the netdash values go in like this:
| Field | netdash value | Notes |
|---|---|---|
server | http://scale-us.netdash.io:2000 | Scheme, host and port only. Use scale-eu.netdash.io:2000 for exits in Europe. |
username | USERNAME-cc-US or USERNAME-cc-US-session-a1b2c3d4-ttl-30 | Your username plus the exit country and, for a sticky session, a session ID and TTL in minutes. |
password | The proxy password from the dashboard | Not your sign-in password. The same for every residential and mobile plan. |
bypass | Optional, for example localhost,127.0.0.1 | Hosts that bypass the proxy and connect directly, see below. |
Playwright documents the credentials as separate username and password fields, so keep server to the scheme, host and port and don't write user:pass@ into it.
Proxy username
USERNAME-cc-US-session-a1b2c3d4-ttl-30
USERNAMEYour username- From the dashboard
-cc-USExit country- ISO code, GB for the UK
-session-a1b2c3d4Session ID- Sticky only; letters and digits
-ttl-30Lifetime- Minutes to keep the exit IP
-session-…-ttl-… for rotating sessions. The password is the proxy password from the dashboard, not your sign-in password.httpCredentials is a different option. It answers HTTP authentication from the website itself (a 401 response), and Playwright's docs note that without an origin it sends the credentials to any server that asks. netdash authenticates at the proxy (a 407 response), so use proxy.username and proxy.password for netdash and keep httpCredentials for sites that need it. Every host, port and username parameter is listed in the proxy format reference.
Keep NETDASH_USERNAME and NETDASH_PASSWORD in a .env file or your secret manager, never in source control or in a chat with an assistant.
One proxy per browser context
A browser context is an isolated session with its own cookies and storage. Give each context its own proxy to run several exit countries or sessions in one browser. The script below opens two contexts through the Europe gateway: a sticky session in Germany and a rotating one in the United Kingdom. Use GB for the United Kingdom, not UK.
import { randomBytes } from "node:crypto";
import { chromium } from "playwright";
const { NETDASH_USERNAME, NETDASH_PASSWORD } = process.env;
const server = "http://scale-eu.netdash.io:2000"; // Europe gateway, Scale plan
const browser = await chromium.launch();
try {
// Sticky: one session ID keeps one exit IP for the 30-minute TTL
const sessionId = randomBytes(4).toString("hex"); // 8 characters, letters and digits
const germany = await browser.newContext({
proxy: {
server,
username: `${NETDASH_USERNAME}-cc-DE-session-${sessionId}-ttl-30`,
password: NETDASH_PASSWORD,
},
});
// Rotating: no session parameters
const unitedKingdom = await browser.newContext({
proxy: { server, username: `${NETDASH_USERNAME}-cc-GB`, password: NETDASH_PASSWORD },
});
for (const [country, context] of [["DE", germany], ["GB", unitedKingdom]]) {
const page = await context.newPage();
await page.goto("https://api.ipify.org?format=json", { timeout: 30_000 });
console.log(country, await page.textContent("body"));
await context.close();
}
} finally {
await browser.close();
}Close a context when its task is done. Cookies set in one context never reach another, which keeps the two countries' sessions apart.
Sticky sessions for multi-step flows
Some flows span several pages: a language choice, a search, then the results pages. If the site ties that state to the visitor's IP address, keep the whole flow on one exit IP with a sticky session. Use one session ID for the context, and a new context with a new ID for the next run. Reusing a session ID keeps the same exit IP for the TTL while that IP stays available.
The TTL is in minutes. The dashboard presets are 15 minutes, 30 minutes, 12 hours and 7 days (-ttl-15, -ttl-30, -ttl-720, -ttl-10080); pick one longer than the flow. The sticky session syntax has the details.
import asyncio
import os
import secrets
from playwright.async_api import Page, async_playwright
async def exit_ip(page: Page) -> str:
await page.goto("https://api.ipify.org?format=json")
return await page.text_content("body")
async def run_flow(urls: list) -> None:
session_id = secrets.token_hex(4) # 8 characters, letters and digits
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context(proxy={
"server": "http://scale-us.netdash.io:2000",
"username": f"{os.environ['NETDASH_USERNAME']}-cc-US-session-{session_id}-ttl-30",
"password": os.environ["NETDASH_PASSWORD"],
})
try:
page = await context.new_page()
page.set_default_navigation_timeout(30_000)
print("start:", await exit_ip(page))
for url in urls:
await page.goto(url)
print(url, await page.title())
await asyncio.sleep(2) # keep the pace gentle for the site
print("end: ", await exit_ip(page)) # same session, same exit IP
finally:
await context.close()
await browser.close()
asyncio.run(run_flow(["https://example.com/", "https://example.com/about"]))Python (sync API)
The sync API takes the same dictionary. Set at launch, the proxy covers the whole browser; set on new_context(), it covers that context.
import os
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://scale-us.netdash.io:2000",
"username": f"{os.environ['NETDASH_USERNAME']}-cc-US",
"password": os.environ["NETDASH_PASSWORD"],
})
try:
page = browser.new_page()
page.goto("https://api.ipify.org?format=json", timeout=30_000)
print(page.text_content("body"))
finally:
browser.close()Playwright Test configuration
In Playwright Test, proxy is a use option, so you can set it once for the whole suite or per project. Projects per country run the same tests from three exit countries. Pair each with a matching locale, so the browser's language settings and the exit IP agree, which is what localization tests usually need.
import { defineConfig } from "@playwright/test";
export default defineConfig({
use: {
proxy: {
server: "http://scale-us.netdash.io:2000",
username: `${process.env.NETDASH_USERNAME}-cc-US`,
password: process.env.NETDASH_PASSWORD,
bypass: "localhost,127.0.0.1",
},
navigationTimeout: 30_000,
},
});import { defineConfig } from "@playwright/test";
// Exits in the Americas use the -us gateway, exits in Europe the -eu gateway.
const markets = [
{ country: "US", gateway: "scale-us.netdash.io:2000", locale: "en-US" },
{ country: "DE", gateway: "scale-eu.netdash.io:2000", locale: "de-DE" },
{ country: "GB", gateway: "scale-eu.netdash.io:2000", locale: "en-GB" },
];
export default defineConfig({
use: { navigationTimeout: 30_000 },
projects: markets.map(({ country, gateway, locale }) => ({
name: country,
use: {
locale,
proxy: {
server: `http://${gateway}`,
username: `${process.env.NETDASH_USERNAME}-cc-${country}`,
password: process.env.NETDASH_PASSWORD,
bypass: "localhost,127.0.0.1",
},
},
})),
});Run one market with npx playwright test --project=DE, or all three with npx playwright test.
Keep local services off the proxy
The bypass field lists hosts that bypass the proxy and connect directly, separated by commas. Add the app under test on localhost, a staging host you operate, or anything else that doesn't need an exit country: the gateway can't reach localhost or hosts on your private network, and direct traffic doesn't count against your balance. For example: bypass: "localhost,127.0.0.1,staging.example.com".
Using Playwright from an AI agent
Playwright MCP lets coding agents drive a Playwright browser. Its --proxy-server flag takes only a server address, so the credentials go in a config file: see Claude Code with Playwright MCP. For Browser Use, the OpenAI Agents SDK and LangChain, see the agent frameworks guide.
Errors and fixes
| Symptom | Cause | Fix |
|---|---|---|
net::ERR_PROXY_CONNECTION_FAILED | Wrong host or port, or a firewall blocks outbound traffic to the gateway port | Copy the endpoint from the proxy format reference and allow outbound TCP to the port (2000 for Scale) |
net::ERR_TUNNEL_CONNECTION_FAILED, or a 407 | Credentials written into server, a wrong password, or a malformed username | Move the credentials to username and password; check the -cc- and session parameters |
| Navigation timeout | No timeout set, or a page that takes longer than the timeout | Set timeout on goto() or navigationTimeout; check the gateway region too: pair US with -us and DE or GB with -eu |
| Wrong exit country | A context reused for another country, or UK instead of GB | Create one context per country; use GB |
| Requests to a local or staging server fail in tests | They went through the proxy, and the gateway can't reach that host | Add the host to bypass, so its traffic skips the proxy |
netdash forwards your traffic unchanged. It doesn't solve CAPTCHAs, render JavaScript or retry requests. Targeting is country-level.
Use it responsibly
Respect robots.txt and each site's terms, keep request rates reasonable and back off when a site slows you down. Don't collect personal data without a lawful basis or open content behind logins or paywalls without authorization. netdash doesn't solve CAPTCHAs or bot challenges, and your scripts shouldn't try to get past them either: if a site rate-limits or challenges your requests, slow down, check its terms, or use its official API. Read how we expect netdash to be used.
Next steps
- Puppeteer proxy setup
The same setup for Puppeteer: --proxy-server plus page.authenticate().
- Country targeting
ISO codes, GB for the United Kingdom, and which gateway to pair with each exit.
- Localization and QA
Check how your site looks and behaves for visitors in other countries.
Also useful: the Selenium guide, browser automation with netdash and rotating and sticky sessions.
Stuck? See troubleshooting or email support with your username (without the password) and the error message.
Email supportSources
Third-party behaviour described on this page was checked against these documents.
- 01Playwright docs: Network, HTTP proxyAccessed 2026-10-09
- 02Playwright API: browserType.launch(), proxy optionAccessed 2026-10-09
- 03Playwright API: browser.newContext(), proxy optionAccessed 2026-10-09
- 04Playwright API: browser.newContext(), httpCredentials optionAccessed 2026-10-09
- 05Playwright for Python: Network, HTTP proxyAccessed 2026-10-09
- 06Playwright for Python API: browser.new_context(), proxyAccessed 2026-10-09
- 07Playwright Test: use options (proxy, projects)Accessed 2026-10-09
Spotted something out of date? Tell us.