Playwright proxy settings that silently do nothing
The setup that looks fine and isn’t
You add a proxy block to your Playwright config, run the scraper, get pages back with no errors, and move on. Then a week later you notice your proxy provider’s dashboard shows almost no bandwidth used, or the target site’s rate limiting kicks in way faster than it should for “rotating” traffic. Nothing crashed. Nothing logged a warning. The proxy just wasn’t in the path.
This happens more than people expect because Playwright doesn’t validate that your proxy config is coherent, it just tries to use whatever you gave it, and in several common cases what you gave it gets quietly ignored rather than rejected. None of what follows is about getting past a site’s defenses. It’s about making sure the traffic actually goes where you told it to before you worry about anything else.
Context-level proxy needs the browser to expect it
Playwright lets you set a proxy at browser.launch(), which applies to every context spawned from that browser, or at browser.newContext(), which is meant to scope a proxy to one context only. The catch, particularly on Chromium running on Windows, is that per-context proxy overrides only take effect if the browser itself was launched expecting that behavior, typically by launching with the proxy server set to the special value per-context rather than a real address. Skip that step and launch normally, then hand different proxies to different newContext() calls, and Chromium doesn’t error out. It just keeps using the browser-level proxy, or no proxy at all if none was set at launch.
This one bites multi-session setups hardest. If the plan is “each context gets its own residential exit IP,” and the launch step wasn’t configured for per-context proxying, every context you thought was isolated is actually sharing one exit address. You usually find out when a target starts throttling what looks like a dozen independent sessions, because from its side, it’s one IP making a dozen sessions’ worth of requests.
Environment variables aren’t proxy settings here
Node reads HTTPS_PROXY and HTTP_PROXY for its own outbound calls, things like axios or the built-in fetch. The browser binaries Playwright drives (Chromium, Firefox, WebKit) run as separate processes with their own network stack, and they don’t inherit those shell environment variables. If a scraper mixes API calls made directly from Node with page navigation done through Playwright, it’s easy to end up with two different network postures in the same run: the API calls go through the proxy because the environment variable is set, and the actual browser navigation doesn’t, because Playwright was never told about a proxy at all. Worth checking both halves of a scraper separately rather than assuming one proxy setting covers the whole run.
The bypass list catches more than intended
The proxy.bypass option takes a comma separated list of hostnames or patterns that the browser should hit directly, skipping the proxy. It’s meant for things like excluding local dev servers. Problems show up when someone adds a broad pattern, something like a wildcard on a shared domain, to skip proxying for one thing, and it ends up matching a subdomain that’s actually part of the scrape target too. The result is a page that loads its main HTML through the proxy but pulls assets or API responses from a subdomain directly, so the target’s own server logs see two different source IPs inside what should be one session. That kind of split is exactly the sort of inconsistency anti-bot systems are built to notice, not because the proxy failed, but because the bypass list quietly carved out part of the traffic.
SOCKS5 with a username and password
This one is a known Chromium limitation, not a Playwright bug: Chromium doesn’t support authentication on SOCKS5 proxies, only on HTTP and HTTPS proxies. A lot of rotating residential and mobile proxy providers issue SOCKS5 endpoints with per-connection credentials, because that’s a natural fit for their rotation model. Plug those straight into Playwright’s proxy config expecting Chromium to send the username and password over SOCKS, and it won’t. Depending on how the proxy server handles an unauthenticated SOCKS connection, you either get an outright failure, which is at least honest, or a connection that goes through anyway on whatever the server falls back to when no credentials show up, which means you no longer know which exit node you’re actually using. If a provider only offers SOCKS5 with auth, check whether they also expose an HTTP endpoint, since that’s usually the more reliable path for a Chromium-based scraper.
Credentials belong in their own fields
Playwright’s proxy object takes server, username, and password as separate fields. Plenty of examples online paste credentials straight into the server URL instead, something like http://user:pass@host:port. That works fine until the password contains a character the URL parser treats as a delimiter, an unescaped @ or : being the usual culprits, and the credential gets silently truncated or misread before it ever reaches the proxy. What you get back is a generic 407 error that looks identical to “you’re out of bandwidth” or “this IP got banned,” which makes it a bad problem to have when you’re also trying to diagnose something else. Passing credentials through their dedicated fields avoids the ambiguity and makes the errors you do get more trustworthy.
One proxy per context, not per request
It’s tempting to treat “rotating proxy” as something Playwright manages for you automatically, but a browser context picks up one proxy configuration and keeps it for as long as that context stays open. Intercepting requests with page.route() changes what a request does, not what network path it takes, so route handlers won’t rotate anything. If the goal is a new exit IP every few requests, that has to be built at the context lifecycle level, closing and reopening contexts on a schedule, not assumed to happen inside a single long-lived page. Anyone running proxy-based scraping at real scale ends up building this rotation logic explicitly, because Playwright has no concept of “rotate mid-session.”
Confirming it’s actually working
Before trusting a scraper run, it’s worth a few minutes of verification rather than assuming the config is doing what it says. Navigate to an IP echo endpoint before hitting anything real and compare the reported address against the proxy’s advertised exit range. Watch the proxy provider’s dashboard during the run for bandwidth or request counts ticking up, since no movement means traffic isn’t routing through it at all. If there’s a test environment with server-side access, checking the logs there for which IP shows up is the most direct confirmation. None of this involves evading anything, it’s just closing the loop on a setting that was already supposed to be active.
Getting the proxy config right doesn’t say anything about whether a target’s own detection will treat the traffic as legitimate, that’s a separate layer entirely and one no config alone controls. But a proxy that’s silently not applying is a problem worth ruling out first, before assuming anything else is wrong.
If you’re comparing proxy types or providers for a Playwright-based scraper, Proxy Scraping covers residential, mobile, and datacenter options along with honest write-ups on how each holds up in practice.
Get new guides and videos first — join the Telegram channel.