Skip to content

ERR_TUNNEL_CONNECTION_FAILED: why the proxy would not open a tunnel

Chrome reached the proxy and asked it to open a tunnel to the site with CONNECT, and the proxy answered with something other than 200 or closed the connection. The status the proxy sent is the real error, and Chrome hides it: a 407 is a login problem, a 403 a refused destination, a 502 or 503 a destination the proxy could not reach. Repeat the request with curl -v to see it.

Reviewed by John Hale. Last updated .

What it looks like

Chrome
This site can't be reached
ERR_TUNNEL_CONNECTION_FAILED

Puppeteer and Playwright
Error: net::ERR_TUNNEL_CONNECTION_FAILED at https://example.com/

What does ERR_TUNNEL_CONNECTION_FAILED mean?

For every HTTPS page, a browser behind an HTTP proxy sends CONNECT example.com:443 and waits for 200 Connection established. Only then does TLS with the site begin. Chromium reports ERR_TUNNEL_CONNECTION_FAILED when that step fails, so nothing reached the website.

One code covers several different refusals. In a normal window Chrome answers a 407 by asking for the proxy's credentials. Headless Chrome and automation tools have no one to ask, so there a missing or wrong proxy password becomes this error too.

Plain http:// pages do not use CONNECT, so the same proxy can load http:// sites and fail every https:// one.

What causes ERR_TUNNEL_CONNECTION_FAILED?

Most likely first.

  1. Missing or wrong proxy credentials. The most common cause in Puppeteer, Playwright and Selenium. Credentials written into --proxy-server are ignored, so Chromium sends none and the proxy answers 407.
  2. The proxy could not reach the site. The site refused or did not answer the exit, the name did not resolve at the proxy, or the exit's IP version does not match the site's. The proxy answers 502, 503 or 504.
  3. The proxy refused the destination or the port. Private addresses, blocked sites and closed ports are refused, usually with 403 or 503. Many proxies allow CONNECT only to port 443 and a few others.
  4. The account cannot carry traffic. An expired plan or used-up traffic, which providers report as 407 or another error to CONNECT.
  5. The wrong proxy type for the port. An HTTP proxy setting pointed at a SOCKS5 port, or at a port that expects TLS, gets no valid answer to CONNECT.
  6. Another proxy in between. A company proxy or security tool that inspects TLS can refuse tunnels to some sites or categories.

How do you see the status Chrome hides?

  1. Repeat the CONNECT with curl

    The < HTTP line right after > CONNECT is the answer Chrome did not show you.

    The proxy's answer to CONNECT
    curl -v -x http://HOST:PORT -U "USER:PASS" https://TARGET/ -o /dev/null 2>&1 \
      | grep -iE "^(> CONNECT|< HTTP|< x-proxy-reason|\* CONNECT)"

  2. Print the CONNECT status in scripts

    CONNECT 200 with a site status means the tunnel works and you are looking at a different error. CONNECT 000 means the proxy closed the connection without answering.

    CONNECT status and site status
    curl -s -o /dev/null -w "CONNECT %{http_connect}, site %{http_code}\n" \
      -x http://HOST:PORT -U "USER:PASS" https://TARGET/

  3. Capture Chrome's own log

    Open chrome://net-export, start logging, reload the page, then stop. Open the file in the NetLog viewer and search the events for the URL and for TUNNEL: the proxy's status line is there.

  4. Test a plain http:// site and the target

    If http://example.com/ loads through the proxy and every https:// site fails, the proxy refuses CONNECT. If only one site fails, check its A and AAAA records and read the target reachability guide.

How do you fix it?

  • 407: give the browser its credentials

    Use the proxy login option of your tool, or IP whitelist authentication for Selenium. Percent-encode special characters only where the credentials go inside a URL.

    Playwright and Puppeteer
    // Playwright
    const browser = await chromium.launch({
      proxy: { server: "http://HOST:PORT", username: "USER", password: "PASS" },
    });
    
    // Puppeteer
    const browser = await puppeteer.launch({ args: ["--proxy-server=http://HOST:PORT"] });
    const page = await browser.newPage();
    await page.authenticate({ username: "USER", password: "PASS" });

  • 403: check the destination

    Use the site's public hostname, not an internal address, and confirm the port.

  • 502, 503 or 504: retry, then widen

    Retry on a new session, then try a wider location such as a country instead of a city. Match the proxy's IP version to the site's addresses.

  • No answer at all: match the type to the port

    Use an HTTP proxy setting with the HTTP port and SOCKS5 with the SOCKS5 port.

If you use TrueProxies

The gateway's answer to CONNECT tells you which of these happened:

TrueProxies answers to CONNECT
AnswerWhat it meansNext step
407 with X-Proxy-ReasonThe login, the targeting options, the plan's state or a limit. The sentence names it.Do what the sentence says; see 407 Proxy Authentication Required.
403, no reasonA private or reserved address, or a destination TrueProxies blocks.Use the site's public hostname.
502 with X-Proxy-ReasonThe site's IP version, a name that does not exist, or no answer through a sticky IP.See 502 Bad Gateway from a proxy.
503, no reasonNo exit free for the targeting, a closed port (mail ports 25, 465, 587 and 2525), or the site refused, reset or did not answer.Retry, then widen the targeting.

Test proxy on the Proxy setup tab sends one request with the settings you selected and reports the result and exit IP, which separates a setup problem from a site problem before you open a browser.

Test the proxy from outside your network

The free proxy checker connects from our servers and reports the failed stage as a code, so you can tell a proxy problem from one on your computer or network.

Run the proxy checker

Questions about ERR_TUNNEL_CONNECTION_FAILED

Why do only HTTPS sites fail?

Only HTTPS sites use the CONNECT tunnel. Plain http:// requests are forwarded without one, so a proxy that refuses CONNECT can still load them.

Why does Puppeteer throw net::ERR_TUNNEL_CONNECTION_FAILED?

Most often the proxy answered 407 because Chromium had no credentials to send. Call page.authenticate() before page.goto(), or use IP whitelist authentication.

Is ERR_TUNNEL_CONNECTION_FAILED caused by the website?

Nothing reached the website. The proxy or the route behind it failed, although the website can be why: for example when it does not answer the exit.

Does clearing the browser cache help?

No. The failure is between the browser and the proxy. Read the proxy's answer instead.