Playwright proxy errors: ERR_TUNNEL_CONNECTION_FAILED and the rest

The proxy answers with a number; Chromium and Firefox turn it into different names, or into a 30-second timeout. Here is which is which.

7 min readTested on Playwright 1.64 · Chromium 156 · Firefox 157
One proxy refusal, two browsers: Chromium and Firefox in Playwright report the proxy's 407, 403 and 465 with different error names.The proxy said one number. The browser says a name.ProxyChromiumFirefox407ERR_HTTP_RESPONSE_CODE_FAILURENS_ERROR_PROXY_CONNECTION_REFUSED403ERR_TUNNEL_CONNECTION_FAILEDNS_ERROR_PROXY_FORBIDDEN465ERR_TUNNEL_CONNECTION_FAILEDNS_ERROR_PROXY_CONNECTION_REFUSEDsocks5 + password: Browser does not support socks5 proxy authenticationPlaywright 1.64 · Chromium 156 · Firefox 157

In short

Pass the proxy login as username and password, not inside server. If a proxied page only times out, the login is wrong. ERR_TUNNEL_CONNECTION_FAILED means the login was fine and the username’s targeting wasn’t. SOCKS5 with a password doesn’t work in Playwright at all; use the HTTP port.

username / password fieldschannel="chromium"

Every error, reproduced

One mistake at a time, same gateway, same page, both browsers. The proxy column is what the gateway answered.

What we brokeProxyChromiumFirefox
Wrong password407Timeout 30000ms exceededNS_ERROR_PROXY_CONNECTION_REFUSED
Login inside server (http://USER:PASS@…)407Timeout 30000ms exceededNS_ERROR_PROXY_CONNECTION_REFUSED
A username value it can’t read (-country-usa)403net::ERR_TUNNEL_CONNECTION_FAILEDNS_ERROR_PROXY_FORBIDDEN
Filters no exit matches (Chicago in Germany)465net::ERR_TUNNEL_CONNECTION_FAILEDNS_ERROR_PROXY_CONNECTION_REFUSED
socks5:// with a password—Browser does not support socks5 proxy authenticationthe same
https:// as the proxy scheme—net::ERR_PROXY_CONNECTION_FAILEDSSL_ERROR_UNKNOWN
A port where nothing listens—net::ERR_PROXY_CONNECTION_FAILEDNS_ERROR_PROXY_CONNECTION_REFUSED
Another gateway’s key (-cc-DE)200loads, exit in the USloads, exit in Russia
No scheme in server200loadsloads
Fig. 1 — Playwright 1.64 for Python, Chromium 156 (default headless shell) and Firefox 157, against via.proxies.vision, October 14, 2026.

Two things stand out. Chromium never names a login problem with the default setup; it waits. And Firefox uses one name, NS_ERROR_PROXY_CONNECTION_REFUSED, for a wrong password, a filter that matches nothing and a closed port alike. The browser error tells you roughly where to look; the proxy’s status tells you what to fix.

Playwright launches Chromium as a slimmed-down “headless shell” by default. When the proxy answered 407 to a wrong password, that shell didn’t fail the navigation; page.goto() waited until Playwright’s 30-second timeout. Launched with channel="chromium", the full browser in new headless mode, the same mistake failed at once:

timeout 30 sp.chromium.launch() · wrong passworddefault headless shell
ERR_HTTP_RESPONSE_CODE_FAILUREp.chromium.launch(channel="chromium") · wrong passwordfails at once

So a proxied scraper whose pages “just time out” most likely has a login problem. Copy username and password again, and launch with channel="chromium" while you debug so the error comes back right away.

The login: 407

Playwright takes the login only from the username and password fields of the proxy settings. A login written into server was not sent in our test, exactly as if there was none. Also check that you didn’t paste a password with a trailing space.

A value in the username: 403

Country, city, network and session go into the proxy username, and our gateway refuses values it can’t read: a three-letter country code, a city with a space, a session label on the rotating port. Firefox calls that NS_ERROR_PROXY_FORBIDDEN, Chromium ERR_TUNNEL_CONNECTION_FAILED. A key it doesn’t know is a different case: -cc-DE was ignored and the page loaded from the US and from Russia. Use -country-de.

Filters no exit matches: 465

The login and the username are fine, but no residential exit fits every filter at once. Drop the narrowest filter first, usually the city. Both browsers hide the number; to see it, run the same login through curl:

bash
curl -sv -x http://via.proxies.vision:9000 --proxy-user "USER-country-de:PASS" https://ipinfo.io/json 2>&1 | grep "< HTTP"

SOCKS5 with a password

Neither browser can authenticate to a SOCKS5 proxy, and Playwright refuses up front: Browser does not support socks5 proxy authentication, raised by new_context() before any page opens. Use the HTTP port of the same proxy with the same login. On our gateway HTTP and SOCKS5 have their own ports, so change scheme and port: http://via.proxies.vision:9000.

Our rotating port gives a new exit per connection, and a browser context keeps its connections open. So pages in one context share an IP, and a new context gets a new one. We loaded an IP checker in each:

1 IPone context, three loadsconnection kept
2 IPstwo contexts, rotating porta new exit each
1 IPtwo contexts, -session-acct1 on :10000the label pins the exit

One context per identity, then: a fresh context when you want a fresh IP, and the same session label when an identity has to survive a context being closed. The same rule holds in Puppeteer.

A setup that works

Login in its own fields, one context per identity. Ran as shown; the output is below.

proxied.py
import os

from playwright.sync_api import sync_playwright

USER = os.environ["PROXY_USER"]
PASS = os.environ["PROXY_PASS"]


def proxy(country: str = "", session: str = "") -> dict:
    """Playwright proxy settings: login in its own fields, never in server."""
    user = USER + (f"-country-{country}" if country else "")
    if session:
        # Sticky port: the same label keeps the same IP for 10 minutes.
        return {"server": "http://via.proxies.vision:10000", "username": f"{user}-session-{session}-lifetime-10", "password": PASS}
    return {"server": "http://via.proxies.vision:9000", "username": user, "password": PASS}


with sync_playwright() as p:
    # channel="chromium" reports proxy errors at once; the default headless
    # shell waited 30 seconds on a wrong password in our test.
    browser = p.chromium.launch(channel="chromium")

    # A new context = a new connection to the proxy = a new IP.
    for _ in range(2):
        context = browser.new_context(proxy=proxy("de"))
        page = context.new_page()
        page.goto("https://api.ipify.org")
        print("new context:", page.inner_text("body"))
        context.close()

    # One identity across contexts: the same session label.
    for _ in range(2):
        context = browser.new_context(proxy=proxy("de", session="account1"))
        page = context.new_page()
        page.goto("https://api.ipify.org")
        print("account1:", page.inner_text("body"))
        context.close()

    browser.close()
txt
new context: 46.114.177.235
new context: 91.22.159.104
account1: 2.245.177.252
account1: 2.245.177.252

Node.js

The same settings in JavaScript: browser.newContext({ proxy: { server, username, password } }). The field names are identical.

Retry dropped pages

An exit is someone’s home connection and can go away mid-page. Catch the navigation error, open a new context and try again once; a 403 or 407 won’t change on a retry, so don’t loop on those.

Why does Playwright time out instead of showing a proxy error?
With the default headless shell, a wrong or missing proxy password made page.goto() wait the full 30 seconds in our test. Launching with channel="chromium" (the new headless mode) reported net::ERR_HTTP_RESPONSE_CODE_FAILURE at once. Check the login first when a proxied page only times out.
What does 'Browser does not support socks5 proxy authentication' mean?
Neither Chromium nor Firefox in Playwright can send a username and password to a SOCKS5 proxy, so Playwright refuses to create the context. Use the proxy's HTTP port with the same login; on via.proxies.vision that is 9000, or 10000 for sticky sessions.
Can I put the username and password into the proxy server URL?
No. In our test http://USER:PASS@host:port as server behaved like no login at all: a timeout in Chromium, NS_ERROR_PROXY_CONNECTION_REFUSED in Firefox. Pass username and password as their own fields.
What does NS_ERROR_PROXY_CONNECTION_REFUSED mean in Playwright Firefox?
Firefox uses it for more than a closed port. A wrong password and a filter no exit matches gave the same name in our test. A username value the proxy can't read gave NS_ERROR_PROXY_FORBIDDEN. Test the same login with curl to see the real status.
How do I get a new IP per page in Playwright?
Use a new browser context per identity. A context keeps its connection to the proxy, so three loads in one context left from one IP in our test, while each new context got a new one. For the same IP across contexts, use the same session label on the sticky port.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.