Skip to content

Guide

Playwright proxy

Playwright takes proxy credentials as ordinary fields, which makes it the least painful of the browser tools for this. The thing worth knowing is that you can set the proxy on a context rather than on the browser, which means one browser can hold several independent sessions on different addresses at once.

Browser level, the simple case

Username and password are fields rather than something smuggled into a URL, so there is no authentication step to forget. This is the whole setup for a single session.

javascript
import { chromium } from 'playwright'

const browser = await chromium.launch({
  proxy: {
    server: 'http://gate-eu.proxies.nullvault.shop:80',
    username: 'USERNAME',
    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()

Context level, several sessions in one browser

A context is an isolated profile with its own cookies and storage. Giving each context its own proxy means one browser process can run several unrelated sessions in parallel, which is far cheaper than launching a browser per session.

javascript
const browser = await chromium.launch()

const first = await browser.newContext({
  proxy: { server: 'http://gate-eu.proxies.nullvault.shop:80', username: 'USER_A', password: 'PASS_A' },
})

const second = await browser.newContext({
  proxy: { server: 'http://gate-eu.proxies.nullvault.shop:80', username: 'USER_B', password: 'PASS_B' },
})

Python is the same shape

The API mirrors the JavaScript one, so the proxy dictionary carries the same three keys and behaves identically.

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        proxy={
            "server": "http://gate-eu.proxies.nullvault.shop:80",
            "username": "USERNAME",
            "password": "PASSWORD",
        }
    )
    page = browser.new_page()
    page.goto("https://api.ipify.org?format=json")
    print(page.text_content("body"))
    browser.close()

Cut the traffic you are not using

On a metered plan a browser is an expensive client, because it faithfully downloads every image, font and tracker on the page. Routing those request types to abort usually removes most of the page weight and changes nothing about the HTML you came for.

javascript
await context.route('**/*', (route) => {
  const type = route.request().resourceType()
  if (type === 'image' || type === 'font' || type === 'media') return route.abort()
  route.continue()
})

What to buy

Residential sticky per session for work that has to pass as a person. Contexts make it cheap to run several of those at once, so buy on traffic rather than on how many browsers you intend to open.

Common questions

Browser level or context level?

Context level unless you have a reason not to. It gives you the same behaviour with the option of running independent sessions side by side, and it costs nothing extra to set it there instead.

Does Playwright need a separate authentication call?

No. Unlike Puppeteer it takes username and password as fields and handles the rest, which removes the single most common failure in browser proxy setups.

Can I change the proxy without restarting the browser?

Not for an existing context, but you can close it and open a new one, which is cheap. That is the normal way to move to a fresh address partway through a run.

Try it against your own target

Metered bandwidth is billed per gigabyte and does not expire, so testing a small amount against the site you actually care about costs very little.

More guides