Documentation Python quickstart Blog Free tools hello@quanticdata.ioLog in

ERR_TUNNEL_CONNECTION_FAILED: How to Fix

A client sends an HTTP CONNECT request to a proxy, which opens a TCP connection to the destination on port 443. ERR_TUNNEL_CONNECTION_FAILED is raised when that tunnel is never established, at one of three points: the client cannot reach the proxy, the proxy refuses the CONNECT, or the proxy cannot reach the destination.

ERR_TUNNEL_CONNECTION_FAILED means your client sent an HTTP CONNECT request to a proxy and never got a working tunnel back. The website was never contacted. In Chromium it is error -111, a deliberate catch-all: it covers a proxy you could not reach, a proxy that refused you, and a proxy that could not reach the target.

What ERR_TUNNEL_CONNECTION_FAILED actually means

When a client fetches an HTTPS URL through an HTTP proxy, it cannot just hand the request over: the request is inside TLS, and the proxy is not the party the certificate belongs to. So the client asks the proxy to become a dumb pipe. That request is the CONNECT method, and it is plain text:

CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Authorization: Basic dXNlcjpwYXNz

HTTP/1.1 200 Connection established

Only after that 200 does the TLS handshake with the real site begin, tunnelled through the proxy. ERR_TUNNEL_CONNECTION_FAILED is what Chromium raises when that exchange does not end in a usable tunnel. The comment above the definition in Chromium's own error list says as much: a tunnel connection through the proxy could not be established.

The distinction that matters, and that almost nobody writes down, is between the neighbouring error codes:

Chromium codeNumberWhat it means
ERR_PROXY_CONNECTION_FAILED-130The proxy itself could not be reached — DNS failed, or the socket to it did not open. Chromium's source explicitly notes this does not include failures during the CONNECT method.
ERR_PROXY_AUTH_REQUESTED-127The proxy asked for authentication in order to establish the tunnel.
ERR_TUNNEL_CONNECTION_FAILED-111The proxy was reached, the CONNECT was attempted, and the tunnel did not come up. This is the general-purpose bucket.
ERR_PROXY_UNABLE_TO_CONNECT_TO_DESTINATION-186The proxy worked but could not connect to the destination — the hostname did not resolve, or the origin refused. Chromium uses it so the proxy is not blamed and marked bad.

If you see -111 rather than -130, the machine reached the proxy. That single fact rules out half of the advice you will find on the subject.

Why the usual advice does not apply to you

On 7 September 2026 we pulled Google's US first page for this error through a residential exit and read every result we could fetch. The ten organic results were two YouTube videos, four forum or Q&A threads, one vendor knowledge-base article, one Microsoft Learn answer and two how-to posts. Six of them returned readable HTML to a plain HTTP client; two served a bot shell instead, and two were videos.

Across those six pages: zero mention the CONNECT request that failed. Zero mention curl, Playwright, Puppeteer, Selenium, SOCKS or a headless browser. Exactly one mentions 407 at all, in a single aside. The advice is uniform and it is written for someone who does not know they have a proxy — disable it, flush DNS, reset the browser. Google's AI Overview on the same page frames it the same way: the browser cannot make a secure connection through a proxy or a VPN.

That is a fine answer for a laptop with a leftover VPN configuration. It is useless if the proxy is there on purpose. And Google knows those people exist: the related searches on that very page include ERR_TUNNEL_CONNECTION_FAILED Playwright. Search the same error with the word "playwright" attached and the result set changes completely — Stack Overflow, a Crawlee issue, a Playwright issue, the r/Playwright subreddit. Two audiences, one error string, and page one only serves the first. The rest of this guide is for the second.

The seven things that produce this error

CauseHow you recognise itFix
Wrong host or portcurl says it failed to connect to the host before any CONNECT line appearsCheck the gateway hostname and port. Rotating, sticky and SOCKS5 endpoints on the same account usually listen on different ports.
No credentials sentThe proxy answers the CONNECT with 407Supply user and password. In a browser launched from code the credentials often have to go somewhere other than the proxy URL — see below.
Malformed username407 again, with credentials that look correctMost residential gateways encode session and country in the username. An unknown country code or a stray separator is rejected as bad auth, not as a bad parameter.
IP not allowlisted403 on the CONNECT, or the connection is closed without an answerAdd the machine's real egress IP. Inside Docker, a CI runner or Kubernetes that is the node's address, not your laptop's.
Destination port not permittedWorks for ordinary https URLs, fails for the same host on a non-standard portMany proxies only allow CONNECT to 443 and 80. Confirm the port is open on the plan before blaming the code.
TLS interception in the middleOnly some sites fail; the error disappears when antivirus HTTPS scanning or the corporate agent is offExclude the browser from the scanner, or install its CA. This is the common cause on a managed Windows machine.
The proxy cannot reach the targetEvery other host works through the same proxy; one host does notRetry through a different exit. Chromium may report -186 here instead, and the proxy is not at fault.

Diagnose it in ten seconds with curl

The browser hides the CONNECT exchange. curl prints it, which is why every diagnosis should start there rather than in the automation code:

curl -v -x http://USER:PASS@PROXY_HOST:PORT https://example.com

Read the verbose output for one specific line and stop:

What curl printsWhat it tells you
Failed to connect to PROXY_HOST port PORT (exit code 7)Nothing is listening, or a firewall is dropping you. The proxy never saw a request. Chromium would call this -130, not -111.
CONNECT tunnel failed, response 407Authentication. Credentials missing, wrong, or a malformed username.
CONNECT tunnel failed, response 403The proxy knows you and is refusing anyway: IP not allowlisted, plan exhausted, or a forbidden destination port.
CONNECT tunnel failed, response 502 or 503The proxy accepted you and could not reach the target. Change exit, not credentials.
Recv failure or an SSL connect error (56, 35)The tunnel opened and then broke — an interception layer, or an exit that died mid-request.

If curl gets a 200 and the browser still fails, the proxy is not the problem and the difference is in how the browser was configured. If curl fails too, fix that first; nothing downstream will work. Our proxy tester runs the same check from a browser when you cannot get to a terminal, and the proxy not working checklist covers the cases that are not tunnel-specific.

Fix it in Chrome, Edge and Opera

For a browser you drive by hand, the error almost always comes from configuration you did not knowingly set. Work through it in this order:

  • Look at the system proxy. Chromium browsers use the operating system's proxy settings, not their own. A VPN or a security tool that was uninstalled carelessly frequently leaves a dead proxy entry behind, and every HTTPS request then dies on a CONNECT to a port nothing is listening on.
  • Turn off HTTPS scanning in your antivirus, temporarily. Products that inspect encrypted traffic terminate the tunnel and rebuild it. When their certificate handling breaks, this is the error you get — and it is why the fix in several vendor forums is to whitelist the browser executable.
  • Test a second browser and a phone on the same network. If only one browser fails, it is that browser's configuration. If everything fails, it is the network or the machine.
  • Capture the evidence. Chromium's built-in net-export log records the CONNECT and the proxy's real answer, which is the difference between guessing and knowing.

Only after all four should you consider resetting network settings or changing DNS. Those steps do sometimes work, but they work by accident, and they will not tell you what was wrong.

Fix it in Playwright, Puppeteer and Selenium

Here the cause is narrower, and it is usually the same one: Chromium will not take proxy credentials from a command-line proxy URL. Passing them there is silently ignored, the proxy answers 407, and the driver surfaces it as net::ERR_TUNNEL_CONNECTION_FAILED. Each library has its own place to put them.

In Playwright, the credentials are separate fields, not part of the server string:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(proxy={
        "server": "http://PROXY_HOST:PORT",
        "username": "USER",
        "password": "PASS",
    })
    page = browser.new_page()
    page.goto("https://example.com", timeout=60000)

In Puppeteer, the proxy goes in the launch arguments and the credentials are answered per page:

const browser = await puppeteer.launch({
  args: ['--proxy-server=http://PROXY_HOST:PORT'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'USER', password: 'PASS' });
await page.goto('https://example.com');

Selenium has no equivalent hook in the plain driver, which is why authenticated proxies there are handled with a wrapper or a small extension; the mechanics are in rotating a proxy in Selenium, and the Puppeteer-specific patterns in using a proxy in Puppeteer.

Three more traps that produce this exact error in automation and nowhere else:

  • Container egress. Code that works on your machine and fails in Docker or Kubernetes is usually an allowlist problem: the node's IP is not the one you registered. Switch that account to username and password authentication, or add the egress range.
  • A per-context proxy overriding the browser one. Playwright lets you set a proxy on the browser and on each context. The context wins, and an incomplete context-level proxy quietly discards the working browser-level credentials.
  • Free or scraped proxy lists. A large share of the questions about this error come from lists where most entries are dead by the time you use them. A dead entry cannot fail any other way — see the 407 guide for how to tell a dead proxy from a rejected one.

When the tunnel is fine and the target is the problem

Once the CONNECT returns 200, the proxy has done its job, and any failure after that belongs to the site. This is worth stating because the two get confused constantly: a tunnel error is never an anti-bot block. Anti-bot systems answer through a working tunnel, with a status code. If your error has changed from ERR_TUNNEL_CONNECTION_FAILED to a 403 or a 429, you have made progress, not gone backwards — from there, 403 Forbidden while scraping and 429 Too Many Requests pick up the thread.

Choosing the right network also removes whole categories of tunnel failure. Ports, session control and allowlisting differ between residential proxies from $0.80/GB, rotating proxies from $0.50/GB and SOCKS5 proxies, which route any TCP traffic rather than only web requests and therefore sidestep the CONNECT method entirely.

Or skip the tunnel entirely

Every failure in this article exists because you are operating the proxy layer yourself. That is the right choice when you need a real browser under your control. It is a poor one when all you actually wanted was the contents of a page.

A scraping API moves the tunnel, the retries, the exit selection and the fingerprint to the other side of an ordinary HTTPS request. Our web scraping API returns any page as Markdown, HTML or structured JSON from $0.0002 per page, $0.001 with JavaScript rendering, and charges only for successes — a failed tunnel is not something you pay for or debug. The same calls are available to coding agents through the MCP server, and every account gets $2 of API usage free each month, which is enough to find out whether the proxy layer was ever worth maintaining.

Sources & further reading

FAQ

Quick answers on err_tunnel_connection_failed.

Something else? Ask us →

Is ERR_TUNNEL_CONNECTION_FAILED a problem with the website?

No. The error is raised before the website is contacted. Your client asked a proxy to open a tunnel with an HTTP CONNECT request and the tunnel never came up, so the request never reached the destination server. If the same URL loads on a different network or with the proxy removed, that confirms it — the site was fine the whole time.

What is the difference between ERR_TUNNEL_CONNECTION_FAILED and ERR_PROXY_CONNECTION_FAILED?

ERR_PROXY_CONNECTION_FAILED (-130) means the proxy itself could not be reached: its name did not resolve, or the socket to it never opened. Chromium's source notes that it explicitly excludes failures during the CONNECT method. ERR_TUNNEL_CONNECTION_FAILED (-111) means the proxy was reached and the CONNECT was attempted, but no usable tunnel resulted. Seeing -111 tells you the network path to the proxy works.

Why does my proxy work in curl but fail in Playwright or Puppeteer?

Almost always because Chromium ignores credentials embedded in a command-line proxy URL, while curl accepts them. The proxy then answers the CONNECT with 407 and the driver reports a tunnel failure. Pass the username and password through the library instead: the proxy username and password fields in Playwright, or page.authenticate in Puppeteer.

Can I pass proxy credentials to Chrome with the proxy-server flag?

No. The flag takes a scheme, host and port only. Credentials placed in that URL are dropped without a warning, which is what makes the resulting error so confusing. Use your automation library's authentication hook, an authenticated local forwarder, or an allowlisted IP so no credentials are needed.

Does ERR_TUNNEL_CONNECTION_FAILED mean my proxy is blocked or banned?

It is not an anti-bot block. Detection systems answer through a tunnel that opened successfully, with a status code such as 403 or 429. A tunnel error means the proxy was unreachable, refused you, or could not reach the destination. It can, however, mean the plan is exhausted or the IP is not allowlisted, which the proxy signals as 403 on the CONNECT.

What error number is ERR_TUNNEL_CONNECTION_FAILED in Chromium?

It is -111 in Chromium's network error list, defined as a tunnel connection through the proxy that could not be established. Its neighbours are useful too: -127 when the proxy requests authentication for tunnel establishment, -130 when the proxy cannot be connected to at all, and -186 when the proxy connected but could not reach the destination.

Stop debugging tunnels

Send an ordinary HTTPS request and get the page back. Proxies, retries and exits are handled on our side, billed per success, with $2 of free usage every month.

Related reading