Using proxies with Playwright: a complete guide

Per-context proxies, credential handling, bandwidth control and the failure modes that waste an afternoon.

JO

Jonas Okonkwo

Developer relations

30 Jun 2026 · 9 min read

Playwright has the best proxy support of the major automation frameworks: credentials are first-class, and proxies can be set per browser context rather than per browser process. That second point is the one that matters for anything at scale.

The basic setup

javascript
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://res.wproxy.io:8000",
    username: "wp-acc4821-country-us-session-a1",
    password: "pass",
  },
});

Per-context is what you want

Launching a browser process per exit costs roughly 80 MB of RAM and a second of startup each. Contexts cost almost nothing and are fully isolated — separate cookies, storage and cache — so one browser can hold dozens of independent identities.

javascript
const browser = await chromium.launch();

async function contextFor(country) {
  const session = Math.random().toString(36).slice(2, 10);
  return browser.newContext({
    proxy: {
      server: "http://res.wproxy.io:8000",
      username: `wp-acc4821-country-${country}-session-${session}`,
      password: "pass",
    },
    locale: country === "de" ? "de-DE" : "en-US",
    viewport: { width: 1440, height: 900 },
  });
}

const contexts = await Promise.all(["us", "gb", "de", "jp"].map(contextFor));

Cut the bandwidth bill

A browser fetches everything a browser fetches: hero images, web fonts, analytics beacons, video preloads. On a metered residential plan you pay for all of it, and for most scraping none of it is data you want.

javascript
const BLOCKED = new Set(["image", "media", "font", "stylesheet"]);

await context.route("**/*", (route) =>
  BLOCKED.has(route.request().resourceType())
    ? route.abort()
    : route.continue(),
);

Typical saving: 60–80%

On an image-heavy retail site, blocking those four resource types took one customer's monthly usage from 1.4 TB to 310 GB with no change in the data extracted. Do check the site still renders — a few sites lazy-load content through CSS.

Failure modes

SymptomCauseFix
ERR_TUNNEL_CONNECTION_FAILEDBad credentials or wrong portTest the same credentials with curl first
Blank page, no errorResource blocking too aggressiveAllow stylesheets and re-test
Works headed, fails headlessEnvironment fingerprintingUse a stealth plugin or run headed in Xvfb
Random mid-session logoutsSticky exit rotatedWatch x-wproxy-session-rotated and re-authenticate
Sudden bandwidth spikeA page started autoplaying videoBlock the media resource type

Verify the exit from inside the page

javascript
const page = await context.newPage();
await page.goto("https://ipinfo.io/json");
console.log(JSON.parse(await page.textContent("pre")));
// { ip: "72.14.201.88", city: "Chicago", country: "US", ... }
Get started

Try it against your own target

One gigabyte free. It is normally enough to find out whether any of this applies to you.