# Using proxies with Playwright: one exit per browser or per context

Playwright takes the proxy at launch or per browser context, with credentials, so each context can be its own sticky session in its own country without restarting the browser.


## Launch-level proxy

**Python**

```python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(proxy={
        "server": "http://res.proxshift.com:9000",
        "username": "USER-cc-us-sid-run1-ttl-30m",
        "password": "PASS",
    })
    page = browser.new_page()
    page.goto("https://api.ipify.org")
    print(page.inner_text("body"))
    browser.close()
```

**Node.js**

```node
import { chromium } from "playwright";

const browser = await chromium.launch({
  proxy: { server: "http://res.proxshift.com:9000", username: "USER-cc-us-sid-run1-ttl-30m", password: "PASS" },
});
const page = await browser.newPage();
await page.goto("https://api.ipify.org");
console.log(await page.innerText("body"));
await browser.close();
```

A browser keeps connections alive, so use a sticky session id at launch: the whole browser then behaves like one visitor from one household for the lifetime you chose.


## Per-context proxies

To run several countries or sessions in one browser, launch with a placeholder proxy and give each context its own. Chromium needs the launch-level proxy for per-context proxies to be honoured.

**Python**

```python
browser = p.chromium.launch(proxy={"server": "http://per-context"})

de = browser.new_context(proxy={"server": "http://res.proxshift.com:9000", "username": "USER-cc-de-city-berlin-sid-de1-ttl-30m", "password": "PASS"})
us = browser.new_context(proxy={"server": "http://res.proxshift.com:9000", "username": "USER-cc-us-state-tx-sid-us1-ttl-30m", "password": "PASS"})
```

**Node.js**

```node
const browser = await chromium.launch({ proxy: { server: "http://per-context" } });

const de = await browser.newContext({ proxy: { server: "http://res.proxshift.com:9000", username: "USER-cc-de-city-berlin-sid-de1-ttl-30m", password: "PASS" } });
const us = await browser.newContext({ proxy: { server: "http://res.proxshift.com:9000", username: "USER-cc-us-state-tx-sid-us1-ttl-30m", password: "PASS" } });
```


## Match the context to the exit

Give each context the locale and time zone of its country, so IP, language and clock agree: `locale=\"de-DE\", timezone_id=\"Europe/Berlin\"` for the Berlin context. Mismatches are a stronger bot signal than the IP itself.


## Save gigabytes: block what you do not need

**Python**

```python
def block(route):
    if route.request.resource_type in {"image", "media", "font", "stylesheet"}:
        return route.abort()
    return route.continue_()

page.route("**/*", block)
```

**Node.js**

```node
await page.route("**/*", (route) => {
  const t = route.request().resourceType();
  return ["image", "media", "font", "stylesheet"].includes(t) ? route.abort() : route.continue();
});
```

A rendered page can weigh 3 to 5 MB; its HTML and API calls a few hundred kilobytes. On a per-GB network this single rule cuts the bill by 80 to 90 %.


## Verify and troubleshoot

- Open an IP-echo service first and check the country; then look at a geolocation lookup for the city.
- A `net::ERR_TUNNEL_CONNECTION_FAILED` or `ERR_PROXY_CONNECTION_FAILED` at launch means credentials or a parameter are wrong; test the same URL with cURL to read the gateway code.
- WebRTC is disabled by default in Playwright contexts only if you block it; add `--disable-webrtc` style hardening or block STUN when privacy matters.


## Questions

**Can each tab have a different proxy?**

Each context can; tabs (pages) inside a context share its proxy. Create one context per session or country.

**Does Playwright support SOCKS5 proxies with authentication?**

Chromium does not accept SOCKS5 credentials; use the HTTP proxy on port 9000 with username and password, or whitelist your server IP and use socks5://res.proxshift.com:9001 without credentials.

Source: https://proxshift.com/guides/playwright-proxy
