If you are driving a real browser with Playwright and running into rate limits, geo-walls, or outright blocks, proxies for Playwright are how you spread that traffic across many IPs instead of hammering a target from one. Playwright happens to have the cleanest proxy support of any major automation tool: it takes a username and password natively, it can run a different IP per browser context, and because it drives a genuine browser engine it clears the hardest anti-bot layers for free.
This guide covers the parts that actually decide success: which proxy type fits Playwright and why, the honest free-versus-paid picture, the exact setup in Python and Node, the per-context trick that lets one browser wear many IPs, and how to keep your automation from getting banned once the proxy is in place.
Why use proxies with Playwright?
People reach for Playwright when a plain HTTP request will not do: the page renders with JavaScript, sets cookies through a flow, or sits behind a bot defense that expects a real browser. That means by the time you want proxies for Playwright, you are usually already on a target that watches IPs closely.
A browser fired from one address, requesting page after page faster than any person browses, trips the same escalation every scraper meets: rate limiting first (a 429 or a slowdown), then an outright IP block, then fingerprint-level scrutiny that serves CAPTCHAs or fake data. Proxies handle the first two by spreading requests across many IPs, so no single address piles up enough activity to look wrong. There is a Playwright-specific reason to care more than a requests-based scraper would: every Playwright session is heavy and stateful. A dead or blocked exit does not cost you one cheap retry, it throws away a whole browser context (its cookies, its login, the seconds you spent loading pages). The price of a bad IP is higher here, not lower.
Geo-testing is the other big driver. If you need to see a site, a price, or an ad exactly as a user in Berlin or Sao Paulo would, you route that context through an IP in that country and Playwright renders the local reality.
One IP, many pages
faster than a person
Rate limit
429 or a slowdown
IP block
address cut off
CAPTCHA / fake data
fingerprint scrutiny
Which proxy type fits Playwright?
Match the proxy to the target, not to a spec sheet. Since Playwright is usually the tool you escalate to for defended sites, residential is the default answer, but not always.
Residential proxies. IPs on real home connections, the kind an ordinary visitor uses. To a website they read as a normal consumer, so they clear the reputation checks that reject hosting IPs on sight. This is the right match for the exact targets that pushed you to Playwright in the first place: retailers, search, social, travel, anything with a real bot team. If you are unsure what these are, what a residential proxy is explains the mechanics. The tradeoff is cost (metered per gigabyte) and some per-exit speed variance, since real home lines are not datacenter-fast.
Datacenter proxies. IPs from hosting providers. Fast, cheap, plentiful, and honest to a fault: a site can tell the IP belongs to a datacenter, so defended targets distrust them. Their place in Playwright work is real though: QA-testing your own app from another region, hitting internal or lightly-defended pages, and any job where the target does not scrutinize IP reputation. Start here whenever the target allows it, because it is the cheapest tier.
Static residential / ISP proxies. Residential-looking but stable, holding one IP for as long as you want. Their Playwright niche is account-bound automation: anything that logs in and has to stay logged in as one consistent identity, where a mid-session IP change would look like a hijack and break the session.
Mobile proxies. IPs from cellular carriers, shared across many real subscribers through carrier NAT. Blocking one risks blocking hundreds of innocent users, so sites almost never do, which makes mobile the heavyweight for the most hostile targets, at the highest price. Overkill for ordinary work, sometimes the only thing that loads the page at all.
| Playwright job | Start with | Escalate to |
|---|---|---|
| Testing your own app across regions | Datacenter | Residential |
| Lightly defended or internal pages | Datacenter | Rotating residential |
| Retail, search, social, travel scraping | Rotating residential | Mobile |
| Logged-in, single-identity automation | Static residential / ISP | Mobile (rarely) |
| The most bot-hostile sites alive | Mobile | Rethink the approach |
The rule inside that table: use the cheapest tier the target tolerates, and move up only when block rates prove you have to. Reaching for mobile on a site that would have accepted residential just burns money.
The free and paid split
Free proxies and Playwright are a bad marriage for production, and it helps to know exactly why. Most free proxies are datacenter IPs that die within minutes, and only a small fraction of any public list is alive at once. Our own free proxy list re-checks and refreshes every few minutes across 100+ countries and HTTP, HTTPS, SOCKS4 and SOCKS5, precisely because the underlying pool churns that fast.
For a requests-based scraper, a dead free proxy is a cheap retry. For Playwright it is expensive: an exit that dies halfway through a flow wastes the whole browser session you built. Stack that on top of the fact that free datacenter IPs are exactly the addresses a defended target already distrusts, and you get the worst case: you spun up a real browser to look human, then wore an IP that screams bot. The one strength Playwright gives you (a genuine browser fingerprint) is thrown away by the weakest possible IP.
Where free is genuinely useful is learning and testing. Wire up your proxy code against the free list, confirm the exit IP changes, test your own app from a few countries, and get the setup right without spending a cent. When you move to real defended targets, paid residential is the honest tool: ours is pay-as-you-go from $0.65/GB with no KYC, so a paused project does not torch a prepaid plan.
How to set up a proxy in Playwright
Here is where Playwright is genuinely nicer than Selenium. You set the proxy at launch, and unlike Chrome's --proxy-server flag, Playwright's proxy object accepts a username and password directly. No local helper, no injected dependency, no native login dialog that hangs your script.
Python:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://203.0.113.10:8080",
"username": "user", # accepted natively, no workaround needed
"password": "pass",
}
)
page = browser.new_page()
page.goto("https://httpbin.org/ip") # shows the exit IP the site sees
print(page.content())
browser.close()
Node:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
proxy: {
server: 'http://203.0.113.10:8080',
username: 'user',
password: 'pass',
// bypass: 'localhost, *.internal.example', // hosts to skip
},
});
const page = await browser.newPage();
await page.goto('https://httpbin.org/ip');
console.log(await page.content());
await browser.close();
})();
Two honest caveats. First, for a SOCKS5 proxy you change the scheme to socks5://, but the browser engines Playwright drives do not support SOCKS proxies that require a username and password. If you need SOCKS5, authenticate that endpoint by allowlisting your machine's IP with the provider instead. Second, if your provider authenticates by IP allowlist rather than credentials, drop the username and password entirely and the same code works with just server.
One browser, many IPs: per-context proxies
This is Playwright's standout feature for proxy work. A browser context is an isolated session (its own cookies, cache and storage), and you can give each context its own proxy. That means one running browser can hold several independent identities, each on a different IP, at the same time. Selenium cannot do this without launching a separate browser per proxy, though Puppeteer manages it through its own browser contexts.
browser = p.chromium.launch(proxy={"server": "per-context"}) # placeholder, see note
ctx_de = browser.new_context(
proxy={"server": "http://203.0.113.10:8080", "username": "u", "password": "p"}
)
ctx_us = browser.new_context(
proxy={"server": "http://198.51.100.42:8080", "username": "u", "password": "p"}
)
page_de = ctx_de.new_page() # one exit
page_us = ctx_us.new_page() # a different exit, same browser
The one gotcha worth knowing: with Chromium you have to pass a proxy at launch for per-context proxies to take effect, even a placeholder like {"server": "per-context"}. Skip it and Playwright errors with Browser needs to be launched with the global proxy the moment a context sets its own. Set the placeholder once and every context's own proxy is honored. Firefox and WebKit do not need it, which matches Playwright's own network docs. This trades cleanly against process weight: contexts are far lighter than whole browsers, so many identities per browser is how you get concurrency out of Playwright without your RAM disappearing.
Sticky vs rotating: match the IP to the flow
The instinct carried over from HTTP scraping is to rotate the IP on every request. For Playwright that instinct is usually wrong, because Playwright work is stateful. A context logs in, sets cookies, paginates, adds to a cart. If the exit IP changed underneath that flow, the site would see one session teleport between addresses mid-visit, which reads as account theft, not a shopper. So the default for Playwright is a sticky IP: one exit held for the life of the context.
You still rotate, just at the right seam. Rotate between contexts and between runs, not within a single flow. Give each context a fresh sticky IP, do the whole logical journey on it, tear the context down, and let the next one draw a new IP. If you are choosing between a rotating pool and pinned static IPs, rotating vs static residential walks through when each wins. For most Playwright scraping, rotating residential with a sticky window per context is the sweet spot.
One thing not to break: geographic consistency. A session whose IP jumps from Germany to Brazil to Japan across three clicks has described itself as a bot in one sentence. Pin a country per context and keep it there for the whole flow.
How many IPs do you actually need? Fewer than an HTTP scraper, because Playwright's concurrency ceiling is lower: each context costs real memory and CPU, so you run tens of them, not thousands. That maps perfectly onto a rotating residential gateway, a single host and port that hands out a fresh sticky exit per context. You point every context at the same endpoint and the pool does the rotating, so there is no IP list to manage in your code.
Avoiding blocks: a proxy fixes the IP, not the fingerprint
This is the part worth being blunt about. A proxy changes the address a site sees. It does nothing about the many other signals that give automation away, and Playwright ships some of those signals by default.
The good news first: because Playwright drives a real browser engine, it wins the hardest layers for free. Its TLS and HTTP/2 handshake genuinely is Chromium's, Firefox's or WebKit's, so the fingerprinting that instantly catches a raw HTTP client does not catch Playwright. That is a real advantage over a requests-based scraper and half the reason you chose it.
The bad news: Playwright-driven Chromium still exposes automation tells at the JavaScript layer, including navigator.webdriver and artifacts of the DevTools connection it runs on, and it moves with a mechanical precision no hand produces. A serious anti-bot system reads all of that no matter how clean your exit IP is. We wrote the full breakdown from the defender's seat in how websites detect proxies: the ASN behind the IP, header order, request pacing, and DNS or WebRTC leaks. Stealth patches such as playwright-stealth and the rebrowser project hide some of the JavaScript flags, and human-like pacing (real delays, randomized, with pauses between logical steps) handles the behavior, but no proxy and no patch makes a bot invisible on a site that truly cares.
One combination to avoid: headless plus a fresh unknown IP. Headless browsers leak automation tells that a normal window does not, and pairing an obviously-automated headless fingerprint with a brand-new residential IP is one of the most suspicious profiles you can present. If a target is genuinely defended, a headed Playwright session on a good residential IP is far less conspicuous than a headless one.
Where to point Playwright
For anything facing real defenses, the exit IP decides the most, so start there. Our residential proxies route through real consumer connections and rotate from a single gateway, which is exactly the shape Playwright wants: one host and port in your proxy object, a fresh believable IP per context, sticky for the length of a flow, and no rotation code to maintain.
Before you trust any exit in a run, confirm what it looks like from outside. Drop it into our proxy checker to read its real exit IP, anonymity grade and speed, and if you want the method behind that, how to check if a proxy is working covers it. Start on the free proxy list to get your Playwright wiring right for nothing, then move to residential at $0.65/GB pay-as-you-go (no KYC) when you point it at targets that bite back. For runs that order their own IPs before the browser starts, the HProxy API docs cover plan creation and delivery over REST. Get the IP right and the proxy side of Playwright stops being the hard part, which frees you to write the automation you actually came to build.
Sources
- Playwright docs, Network: HTTP Proxy (proxy object fields
server,username,password,bypass; per-context proxy; theper-contextlaunch placeholder for Chromium). - Playwright API, BrowserType.launch proxy option (native username and password authentication, SOCKS support).