How to Use a Proxy with Playwright (Python and Node.js)

Short answer: pass a proxy object with server, username and password to browser.launch() to proxy the whole browser, or to browser.new_context() to proxy a single context. Keep credentials out of the server URL, and verify the exit IP before you run anything real.

This guide shows both patterns in Python and Node.js, how to save bandwidth on per-GB plans, how to test localized pages, and how to fix the most common errors. It is part of our guide to proxies for web scraping. If you work with plain HTTP clients instead, see Python requests proxy.

Use proxies for public data and legitimate testing only: market research, price monitoring, ad verification, SEO checks and QA. Respect each site’s terms and robots.txt.

Prerequisites

  • Python: pip install playwright then playwright install chromium
  • Node.js: npm init playwright@latest or npm i playwright then npx playwright install chromium
  • Proxy credentials from your dashboard: username, password, host and port. New to these terms? Read what a proxy host and port are.

Proxy the whole browser (Python)

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(
        proxy={
            "server": "http://{PROXY_HOST}:{PORT}",
            "username": "USERNAME",
            "password": "PASSWORD",
        }
    )
    page = browser.new_page()
    page.goto("https://httpbin.org/ip", timeout=60_000)
    print(page.inner_text("body"))
    browser.close()

The printed origin should be the proxy’s IP, not yours. For more ways to check a proxy, see how to test proxies.

Proxy the whole browser (Node.js)

import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: {
    server: "http://{PROXY_HOST}:{PORT}",
    username: "USERNAME",
    password: "PASSWORD",
  },
});

const page = await browser.newPage();
await page.goto("https://httpbin.org/ip", { timeout: 60000 });
console.log(await page.innerText("body"));
await browser.close();

One proxy per context

A browser context is an isolated session with its own cookies and storage. You can give each context its own proxy, which is useful when you compare what different locations see, or when each task needs its own IP.

from playwright.sync_api import sync_playwright

PROXIES = [
    {"server": "http://{PROXY_HOST}:{PORT}", "username": "USERNAME", "password": "PASSWORD"},
    {"server": "http://{PROXY_HOST_2}:{PORT_2}", "username": "USERNAME", "password": "PASSWORD"},
]

with sync_playwright() as p:
    browser = p.chromium.launch()
    for proxy in PROXIES:
        context = browser.new_context(proxy=proxy)
        page = context.new_page()
        page.goto("https://httpbin.org/ip", timeout=60_000)
        print(page.inner_text("body"))
        context.close()
    browser.close()

Node.js is the same: browser.newContext({ proxy }). Very old Playwright versions required a placeholder proxy at launch for Chromium per-context proxies, so update to a recent release if the per-context proxy is ignored.

Bypass the proxy for some hosts

proxy={
    "server": "http://{PROXY_HOST}:{PORT}",
    "username": "USERNAME",
    "password": "PASSWORD",
    "bypass": "localhost,.internal.example.com",
}

Hosts matching bypass connect directly. This is handy for local test servers and internal APIs.

Save bandwidth on per-GB plans

A headless browser downloads everything a page references. On per-GB plans such as residential proxies, block what you do not need:

BLOCKED = {"image", "font", "media"}

def route_handler(route):
    if route.request.resource_type in BLOCKED:
        route.abort()
    else:
        route.continue_()

context = browser.new_context(proxy=proxy)
context.route("**/*", route_handler)

Only block what your task does not need. Some pages render differently, or break, without images or fonts. Measure the effect on a small sample first. With time-based plans such as USA 4G/5G mobile proxies (unlimited bandwidth), blocking is optional.

Geo-testing: match locale and timezone

For QA of localized content, set the context locale, timezone and geolocation to match the proxy’s location, so what you see reflects a real visitor from that place:

context = browser.new_context(
    proxy=proxy,
    locale="en-US",
    timezone_id="America/New_York",
    geolocation={"latitude": 40.7128, "longitude": -74.0060},
    permissions=["geolocation"],
)

Use this for legitimate checks such as localized prices, ads and content. Also check that your browser is not exposing a different IP through WebRTC, see how to disable WebRTC in Chrome.

Rotation

If your plan gives you a rotating gateway, one proxy configuration is enough, and new IPs appear on the schedule or per connection that you set. USA 4G/5G proxies rotate every 5, 10 or 30 minutes depending on your setting. If you need the same IP across several page loads, use a sticky session or a static IP and keep it within one context. Check your dashboard for the exact options. For trade-offs, see rotating vs static residential proxies.

Handle failures

from playwright.sync_api import Error, TimeoutError as PWTimeout
import random
import time

def goto_with_retry(page, url, retries=3):
    for attempt in range(retries):
        try:
            response = page.goto(url, timeout=60_000, wait_until="domcontentloaded")
            if response and response.status in (429, 503):
                time.sleep(2 ** attempt + random.random())
                continue
            return response
        except (PWTimeout, Error) as exc:
            print(f"attempt {attempt + 1} failed: {exc}")
            time.sleep(2 ** attempt + random.random())
    raise RuntimeError(f"Failed after {retries} attempts: {url}")
SymptomLikely causeFix
net::ERR_PROXY_CONNECTION_FAILEDWrong host or port, or proxy unreachableVerify {PROXY_HOST} and {PORT}, test from the terminal
net::ERR_TUNNEL_CONNECTION_FAILEDProxy refused the tunnel, often bad credentials or a blocked destinationCheck username and password and the whitelist, try another target
Navigation fails right after launchCredentials in the server URL instead of separate fieldsUse username and password keys
TimeoutErrorSlow target or heavy pageRaise the timeout, use domcontentloaded, block heavy resources, retry with backoff
HTTP 429Too many requests to the targetLower concurrency, add delays, back off
Proxy ignored in a contextOld Playwright versionUpdate Playwright and browsers

The exact error text for wrong credentials can vary between Chromium versions, so treat the table as a starting point and check the Playwright log output with DEBUG=pw:api (Node.js) or PWDEBUG=console if needed.

Next steps

Once one page loads through the proxy, scale gradually and watch your error rate and bandwidth. For choosing between residential, mobile and ISP proxies and estimating cost, read proxies for web scraping.

Need proxies to test with? Compare residential proxies (per GB), USA 4G/5G mobile proxies (per day, unlimited bandwidth) and static ISP proxies (30-day plans), or see the pricing page. Every plan comes with a 24-hour money-back guarantee.

See residential plans

FAQ

How do I set a proxy in Playwright?

Pass a proxy option with server, username and password to launch() for the whole browser, or to new_context() (newContext() in Node.js) for one context.

Can I use a different proxy for each Playwright context?

Yes. Create one context per proxy with browser.new_context(proxy=...). Each context also has isolated cookies and storage.

How do I use proxy authentication in Playwright?

Set the username and password fields of the proxy option instead of embedding them in the server URL.

Does Playwright support SOCKS proxies?

Yes, Chromium accepts a socks5://host:port server value. Chromium does not support SOCKS5 with username and password authentication, so use an HTTP proxy for authenticated setups or IP whitelisting for SOCKS.

How can I reduce proxy bandwidth in Playwright?

Block images, fonts and media with route(), wait for domcontentloaded instead of full load, and cache pages you have already fetched.

Why is my Playwright proxy not working?

Check host, port and credentials, make sure you are on a recent Playwright version, and confirm the exit IP with a simple page such as httpbin.org/ip before testing your real target.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.