Skip to content
Automation

Playwright Proxies: Authentication, Per-Context Rotation and Real Geo Testing

Playwright accepts a proxy at two levels — when launching the browser and when creating a context — and the choice between them decides whether you can run more than one identity per process. Setting it on the context is almost always right: contexts are isolated, cheap, and each one can carry its own exit IP, locale, timezone and cookie jar. That combination is what makes Playwright a credible tool for geo-sensitive work rather than just a headless renderer.

seamless Team11 min readAugust 9, 2026
  • playwright
  • automation
  • browser automation
  • geo-targeting
Three isolated glass-walled chambers side by side, each with its own outgoing routing line, the middle one lit crimson

The setup itself is short — the Playwright integration page has the copy-paste version. This article covers the parts that decide whether a Playwright job survives contact with a real target.

Browser-level versus context-level

javascript
const { chromium } = require("playwright");

// One exit for the whole browser process.
const browser = await chromium.launch({
  proxy: { server: "http://PROXY_HOST:PROXY_PORT" },
});

// One exit per context — this is the useful one.
const context = await browser.newContext({
  proxy: {
    server: "http://PROXY_HOST:PROXY_PORT",
    username: "USERNAME",
    password: "PASSWORD",
  },
});

Context-level configuration means a single browser process can run ten identities in parallel, each with its own exit, cookies and storage. Launching ten browsers to achieve the same thing costs roughly ten times the memory for no benefit.

On some older Chromium builds a browser-level proxy must be present for per-context proxies to work at all. If context proxies appear to be ignored, launch with a harmless placeholder such as per-context as the server and set the real values on each context.

Credentials: fields, not URLs

Chromium's --proxy-server flag takes no credentials. That limitation is why Puppeteer needs an explicit authentication step, and Playwright's contribution is to handle it for you — but only if you use the username and password fields. Embedding them in the server string produces a silent 407 that looks like a network failure.

javascript
// Wrong: Chromium drops the userinfo, the request is unauthenticated.
proxy: { server: "http://USERNAME:PASSWORD@PROXY_HOST:PROXY_PORT" }

// Right: Playwright answers the proxy's auth challenge for you.
proxy: {
  server: "http://PROXY_HOST:PROXY_PORT",
  username: "USERNAME",
  password: "PASSWORD",
}

Rotation across contexts

Because the exit is selected by the username, rotation in Playwright is a matter of generating a new session identifier per context and disposing of the context when the job is done.

javascript
const crypto = require("crypto");

async function withFreshExit(browser, country, fn) {
  const tag = crypto.randomBytes(4).toString("hex");
  const context = await browser.newContext({
    proxy: {
      server: "http://PROXY_HOST:PROXY_PORT",
      username: `USERNAME-country-${country}-session-${tag}`,
      password: "PASSWORD",
    },
    locale: country === "de" ? "de-DE" : "en-US",
    timezoneId: country === "de" ? "Europe/Berlin" : "America/New_York",
  });

  try {
    return await fn(context);
  } finally {
    await context.close();   // frees the exit and the cookie jar together
  }
}
Closing the context is what prevents cookies from one identity following another.

The mismatch that gives you away

A German exit IP serving a browser reporting en-US, America/New_York and a Californian geolocation is a stronger bot signal than the datacenter IP you were trying to avoid. Real users are internally consistent; the value of a geo-targeted exit disappears if the browser contradicts it.

SignalWhere it comes fromSet it with
Exit IP countryProxy gatewayUsername targeting
navigator.languageBrowser contextlocale
Intl timezoneBrowser contexttimezoneId
Geolocation APIBrowser contextgeolocation + permission
Accept-LanguageRequest headerslocale sets it too
Currency and pricingThe siteFollows from the above
javascript
const context = await browser.newContext({
  proxy: { server: "http://PROXY_HOST:PROXY_PORT", username: "USERNAME-country-de", password: "PASSWORD" },
  locale: "de-DE",
  timezoneId: "Europe/Berlin",
  geolocation: { latitude: 52.52, longitude: 13.405 },
  permissions: ["geolocation"],
  viewport: { width: 1440, height: 900 },
});

City-level exits for geo testing

Target a country, region or city and see the page exactly as a local visitor does. No surcharge for city targeting.

Browse locations

Verify the exit inside the browser

Trusting the configuration is how silent fall-through to a direct connection goes unnoticed. Check from inside the page, because that is the path the target actually sees.

javascript
const page = await context.newPage();
await page.goto("https://api.ipify.org?format=json");
console.log(await page.locator("pre").innerText());

Things that waste an afternoon

  • `--proxy-server` in `args`. Playwright's proxy option is the supported path; the raw flag bypasses the credential handling.
  • `NO_PROXY` in the environment. It applies to Playwright too and will quietly exclude the host you care about.
  • Reusing one context for everything. Cookies, storage and service workers persist, so identity two inherits identity one.
  • Ignoring `context.close()`. Long-running jobs leak memory and hold sticky exits far longer than intended.
  • Loading images you never look at. Route interception to block images and fonts typically cuts bandwidth by more than half, which matters when you pay per GB.
javascript
await context.route("**/*", (route) => {
  const type = route.request().resourceType();
  return ["image", "font", "media"].includes(type)
    ? route.abort()
    : route.continue();
});
On residential bandwidth this is usually the single largest cost saving available.

Block carefully, though: some targets check that the assets they served were actually fetched. If a page starts failing only after you add interception, that is the first thing to reverse.

Playwright gives you honest browser behaviour by default, which is most of the battle. The remaining work is making sure everything the browser reports agrees with where the traffic appears to come from — and that is a configuration problem, not an evasion problem.

Frequently asked questions

How do I use an authenticated proxy in Playwright?

Pass server, username and password as separate fields in the proxy option. Credentials embedded in the server URL are dropped by Chromium and the request goes out unauthenticated, which the proxy answers with a 407.

Should I set the proxy on the browser or the context?

On the context in almost every case. Context-level proxies let one browser process run many identities in parallel, each with its own exit IP, cookies and locale, at a fraction of the memory cost of launching separate browsers.

How do I rotate proxies in Playwright?

Generate a new session identifier in the proxy username for each context, and close the context when the task finishes. The gateway assigns a new exit for the new identifier; nothing in the browser needs to change.

Why does my Playwright script still show my real IP?

Usually the proxy was set on a context you are not using, or the option was passed through args rather than the proxy field, or NO_PROXY in the environment excluded the host. Navigate to an IP echo service inside the page to confirm which path the traffic actually takes.

Do I need to change locale and timezone when using a foreign proxy?

Yes, if you want the session to look plausible. A German exit IP paired with an en-US browser reporting a New York timezone is an obvious inconsistency, and it also changes what the site serves you — which defeats the purpose of geo testing.

Does blocking images break scraping?

Usually not, and it saves a great deal of bandwidth. Some bot-protection layers do verify that expected assets were requested, so if pages start failing right after you add route interception, that is the change to undo first.

SE
seamless Team
Proxy infrastructure

The seamless team runs residential, ISP and datacenter proxy infrastructure and writes these guides from day-to-day operational experience.

Ready to try seamless proxies?

Residential, ISP and datacenter proxies with no data expiry.

Browse Plans