We use cookies to enhance user experience, personalize content, and analyze traffic. Cookie Policy

← Back to all articles

Puppeteer Proxy Errors: ERR_PROXY_CONNECTION_FAILED, 407

Fix Puppeteer proxy errors: net::ERR_PROXY_CONNECTION_FAILED, ERR_TUNNEL_CONNECTION_FAILED, ERR_NO_SUPPORTED_PROXIES, 407 auth, and SOCKS5 failures.

by Unknown Proxies

14 min read

June 26, 2026

Updated October 17, 2026

Puppeteer Proxy Errors: ERR_PROXY_CONNECTION_FAILED, 407

Most Puppeteer proxy errors come from four mistakes: a wrong host, port, or scheme in --proxy-server, credentials put inside the proxy URL, a SOCKS5 proxy that needs a password, or a bypass rule that sends traffic direct. Chrome reports these as net:: errors such as net::ERR_PROXY_CONNECTION_FAILED or net::ERR_TUNNEL_CONNECTION_FAILED. You see them as the person running the script, not as a visitor, and each name points to a different layer. Read the exact error before you change code. The fixes below match the Puppeteer docs as of October 2026.

Quick fixes:

  1. Test the proxy outside the browser with curl -x. If curl fails, Puppeteer is not the problem.
  2. Put only scheme://host:port in --proxy-server. Chrome ignores a user:pass@ part.
  3. Send credentials with page.authenticate({ username, password }) before page.goto().
  4. Do not use SOCKS5 with a username and password. Chrome does not support SOCKS5 authentication. Use the HTTP endpoint of the same proxy.
  5. Keep the target domain out of --proxy-bypass-list.

If you have not set up the proxy yet, start with the Puppeteer proxy setup guide for --proxy-server and page.authenticate(). This page covers what to do when that setup fails.

Puppeteer proxy errors: quick reference

The error names below come from Chromium's net_error_list.h. Puppeteer shows them in the message that page.goto() throws, for example Error: net::ERR_PROXY_CONNECTION_FAILED at https://example.com.

Error in Puppeteer What Chromium means Common Puppeteer cause First check
net::ERR_PROXY_CONNECTION_FAILED Could not resolve the proxy host or open a socket to it Wrong host or port, proxy down, firewall blocks the port curl -x with the same host and port
net::ERR_TUNNEL_CONNECTION_FAILED Connected to the proxy, but the HTTPS tunnel (CONNECT) failed Missing or ignored credentials, proxy refused the destination, wrong protocol Test an HTTPS URL with curl -x, then check auth
net::ERR_NO_SUPPORTED_PROXIES No usable proxy in the configured list user:pass@ in the proxy value, a bad scheme, or stray quotes Print the exact --proxy-server value
net::ERR_INVALID_AUTH_CREDENTIALS Credentials could not be established during HTTP auth Wrong username or password in page.authenticate() Same credentials with curl -x
HTTP 407 in response.status() The proxy wants valid credentials page.authenticate() missing or called too late Call it before page.goto()
net::ERR_SOCKS_CONNECTION_FAILED Could not set up the connection through the SOCKS proxy SOCKS5 endpoint needs a password, or the port is not SOCKS Use the HTTP endpoint, or an IP-allowlisted SOCKS port
TimeoutError: Navigation timeout of 30000 ms exceeded The page did not reach the wait condition in time Slow proxy route, heavy page, or blocked route Load a small IP echo page through the proxy
Page shows your own IP Chrome sent traffic direct Flag missing, target in bypass list, or wrong browser instance Log launch args and visit an IP echo page
Target returns 403 or 429 The proxy works, and the site made a decision Rate, reputation, or access rules See the target-side section below

The main split is proxy layer versus target layer. If Chrome cannot connect to the proxy, the target never sees the request. If the target returns 403 or 429, the proxy path already works.

Decision tree for diagnosing Puppeteer proxy server errors by layer

Test the proxy outside Puppeteer first

Test the same proxy with curl before you change Puppeteer code:

curl -x "http://USERNAME:PASSWORD@proxy.example.com:8080" https://ipv4.unknownproxies.com/ip

If that fails, check the proxy host, port, protocol, username, password, plan status, and IP allowlist. Also check whether the provider gave you an HTTP or a SOCKS5 endpoint.

If curl succeeds, test a normal HTTPS page:

curl -I -x "http://USERNAME:PASSWORD@proxy.example.com:8080" https://example.com

This catches proxy routes that reach an IP echo service but fail to open a tunnel to a normal HTTPS site. It is also faster than launching a browser for every guess. Add -v to see the CONNECT request and the proxy's reply, including a 407 or 403 from the proxy itself.

Chrome ignores user:pass in --proxy-server

This is a common cause of ERR_TUNNEL_CONNECTION_FAILED and auth errors in Puppeteer. A URL such as --proxy-server=http://user:pass@host:port looks correct, because curl and Python accept that format. Chrome does not. The Chromium proxy documentation says Chrome "will not use any credentials embedded in the proxy settings." The proxy then answers with 407, and Chrome has no credentials to send.

Put only the scheme, host, and port in the flag, and send credentials with page.authenticate():

import puppeteer from "puppeteer";

const browser = await puppeteer.launch({
  headless: true,
  args: ["--proxy-server=http://proxy.example.com:8080"],
});

const page = await browser.newPage();

await page.authenticate({
  username: process.env.PROXY_USERNAME ?? "",
  password: process.env.PROXY_PASSWORD ?? "",
});

await page.goto("https://ipv4.unknownproxies.com/ip", {
  waitUntil: "domcontentloaded",
  timeout: 30_000,
});

If your provider gives you host:port:username:password, split it into a server value (http://host:port) and separate username and password fields. The proxy converter reformats a whole list.

The Puppeteer MCP server never calls page.authenticate(), so an AI agent using it runs into this limit with any authenticated proxy. The Puppeteer MCP proxy guide covers the local forwarder that fixes it.

Older examples use headless: "new". Current Puppeteer accepts only true, false, or "shell", and true already runs the new headless mode.

Fix net::ERR_PROXY_CONNECTION_FAILED

Chromium uses this error when it cannot create a connection to the proxy server. Either the proxy hostname did not resolve, or the socket connection to it failed. It does not cover failures during the CONNECT step. Those show as ERR_TUNNEL_CONNECTION_FAILED.

Common causes:

Do not rotate through a full list yet. Pick one proxy, test it with curl, then launch one browser with one page. If one clean proxy cannot load an IP echo page, a larger pool only makes the logs noisier.

If the same error appears in a normal Chrome window, use the Chrome ERR_PROXY_CONNECTION_FAILED guide to check system settings, extensions, and managed policy. For a client-agnostic test sequence, see the broader proxy error troubleshooting guide.

Fix net::ERR_TUNNEL_CONNECTION_FAILED

For an HTTPS site, Chrome asks an HTTP proxy to open a tunnel with the CONNECT method. This error means Chrome reached the proxy, but the tunnel was not set up. Check these causes in order:

  1. Credentials. Credentials in the --proxy-server URL are ignored. Use page.authenticate() and confirm the same credentials work with curl -x.
  2. Proxy refused the destination. Some proxies block certain domains or ports and answer the CONNECT with an error. Run curl -v -x ... https://target and read the proxy's status line.
  3. Protocol mismatch. An http:// scheme pointed at a SOCKS-only port cannot complete CONNECT. Use the scheme and port that the provider documents for HTTP.
  4. Upstream route failure. The proxy could not reach the target. Try a second proxy from the same plan against the same URL.

An HTTP proxy reaches HTTPS sites through CONNECT, so the proxy URL usually stays http://proxy.example.com:8080 even when the target is https://. Do not change the scheme to https:// unless your provider runs a TLS proxy endpoint.

Fix net::ERR_NO_SUPPORTED_PROXIES

Chromium returns this error when none of the configured proxies can be used. In Puppeteer it nearly always means the --proxy-server value is malformed. The causes people report in the Puppeteer issue tracker, most common first:

  1. Credentials in the URL. --proxy-server=http://user:pass@host:port triggers this error in the reports there. Strip the user:pass@ part and use page.authenticate().
  2. Literal double quotes in the argument. A value like --proxy-server="host:port" passed as one array item includes the quote characters. The shell removes quotes on the command line, but args: [...] does not. Write --proxy-server=host:port with no inner quotes.
  3. A bad scheme or prefix. Use http://, https://, socks4://, or socks5://. A malformed prefix such as http=socks5://host:port can be ignored or rejected.
  4. A built string that came out wrong. Template strings can produce --proxy-server=undefined or an empty value when an environment variable is missing. The value must also be one server, not a host:port:user:pass string or a JSON object turned into a string.

Log browser.process()?.spawnargs after launch to see the arguments Chrome actually received. In a normal Chrome window the same error usually comes from system proxy settings instead, which the Chrome ERR_PROXY_CONNECTION_FAILED guide covers.

Fix 407 and net::ERR_INVALID_AUTH_CREDENTIALS

HTTP 407 means the proxy wants valid credentials before it forwards the request. It is a proxy-layer error, not a website login problem. net::ERR_INVALID_AUTH_CREDENTIALS is how Chrome reports that it could not complete HTTP authentication with the credentials it had.

Fixes, in order:

  1. Set only scheme, host, and port in --proxy-server.
  2. Call page.authenticate() on every page that navigates, before page.goto(). Pages that browser.newPage() creates later need their own call.
  3. Confirm the same username and password work with curl -x.
  4. Remove copied spaces, line breaks, or invisible characters.
  5. Do not URL-encode the values for page.authenticate(). Pass the raw username and password.
  6. Confirm the plan is active and the endpoint matches the credentials, for example the right region or session format.

Puppeteer documents that it turns on request interception internally to implement page.authenticate(). If you also use page.setRequestInterception(true) with your own handler, make sure every request is continued or aborted, or navigation can hang.

If curl works but Puppeteer still shows an auth prompt, run headed mode (headless: false). Then you can see whether Chrome is asking for proxy credentials or the website is asking for a site login. For more on the status code itself, read 407 Proxy Authentication Required.

SOCKS5 auth is not supported by Chrome

Puppeteer accepts a SOCKS5 proxy in the flag:

const browser = await puppeteer.launch({
  args: ["--proxy-server=socks5://proxy.example.com:1080"],
});

That only works when the SOCKS5 endpoint does not require a username and password. The Chromium proxy documentation states: "No authentication methods are supported for SOCKSv5 in Chrome." page.authenticate() does not help either, because it answers HTTP auth challenges, and SOCKS5 authentication happens before any HTTP request. The usual result is net::ERR_SOCKS_CONNECTION_FAILED or a failed connection.

You have three options:

Chrome resolves hostnames through the SOCKS5 proxy, so DNS for the target goes through the proxy too.

Check bypass rules and per-context proxies

A broad bypass list can send target traffic direct, so the page shows your own IP. Keep bypass rules narrow:

const browser = await puppeteer.launch({
  args: [
    "--proxy-server=http://proxy.example.com:8080",
    "--proxy-bypass-list=localhost;127.0.0.1",
  ],
});

Chrome accepts semicolons or commas between bypass rules. It already sends localhost and loopback addresses direct by default, so you only need those entries if you want to be explicit.

Puppeteer can also set a proxy for one browser context with browser.createBrowserContext():

const context = await browser.createBrowserContext({
  proxyServer: "http://proxy.example.com:8080",
  proxyBypassList: ["localhost"],
});

const page = await context.newPage();
await page.authenticate({ username: "user", password: "pass" });
await page.goto("https://ipv4.unknownproxies.com/ip");

The proxyServer option takes a server with an optional port. The Puppeteer docs say the username and password go through page.authenticate(), so the same credential rules apply. Older code calls createIncognitoBrowserContext(), which current Puppeteer releases replaced with createBrowserContext().

Fix navigation timeouts without hiding the real error

A navigation timeout can mean the proxy is slow, the target is slow, the route is blocked, or the page waits for assets that never finish.

Start with a short, explicit timeout and a light page:

await page.goto("https://ipv4.unknownproxies.com/ip", {
  waitUntil: "domcontentloaded",
  timeout: 30_000,
});

Then test the real target with the same proxy. If the IP echo page loads fast and the target times out, the proxy is reachable. The cause may be target-side filtering, page weight, blocked assets, or too much concurrency.

Do not fix every timeout by raising timeout to several minutes. Worker queues pile up, browser processes stay open, and retry storms stay hidden. Log enough context to separate slow proxies from target-side failures:

Field to log Why it matters
Proxy ID or endpoint Finds one bad route quickly
Target host Separates global proxy issues from one domain
Browser mode Makes headed and headless differences easy to compare
Error name or status code Separates ERR_TUNNEL_CONNECTION_FAILED, 407, 403, 429, and timeouts
Retry count Catches loops that create their own blocks
Elapsed time Shows whether failures are instant or slow

Instant failures usually point to the proxy layer: a refused connection, a bad scheme, or rejected auth. Slow failures point to latency, a heavy page, or a target that holds the connection open.

Visual flow for logging Puppeteer proxy server troubleshooting data

Certificate errors through a proxy

An ordinary HTTP proxy tunnels TLS and never sees the page, so the site's own certificate reaches Chrome untouched. If you see net::ERR_CERT_AUTHORITY_INVALID through a proxy, the proxy is probably decrypting and re-signing traffic. That is normal for a debugging proxy you run yourself or a corporate inspection proxy, and unusual for a provider endpoint.

For your own inspection proxy, add the proxy's root certificate to the machine's trust store. As a short-lived test, Puppeteer's acceptInsecureCerts option ("Whether to ignore HTTPS errors during navigation", default false) turns the check off:

const browser = await puppeteer.launch({
  acceptInsecureCerts: true,
  args: ["--proxy-server=http://127.0.0.1:8888"],
});

Do not leave this on in production. It also hides a real man-in-the-middle.

403 and 429 are not proxy setup errors

If the target returns 403 or 429, Puppeteer reached the site. The response may still relate to proxy reputation or request concentration, but the proxy configuration works.

For 403, compare:

To compare a working browser session with the Puppeteer session, save a HAR file from each and open them in the HAR analyzer. It lists status codes, redirects, and blocked requests side by side with timing.

For 429, slow down before you add more IPs. Puppeteer loads many resources per navigation, so one page.goto() can send far more requests than the script suggests. For the status codes themselves, see HTTP 403 Forbidden causes and handling HTTP 429 rate limits.

Rotate only after one proxy works

Rotation is not a first fix. If every proxy fails because the format is wrong or credentials are missing, rotation repeats the same error with more noise.

Once one proxy works, rotate at a browser or context boundary:

for (const proxy of proxies) {
  const browser = await puppeteer.launch({
    headless: true,
    args: [`--proxy-server=${proxy.server}`],
  });

  try {
    const page = await browser.newPage();

    if (proxy.username && proxy.password) {
      await page.authenticate({
        username: proxy.username,
        password: proxy.password,
      });
    }

    await page.goto("https://example.com", {
      waitUntil: "domcontentloaded",
      timeout: 30_000,
    });
  } finally {
    await browser.close();
  }
}

For rotation patterns that reuse one browser, the same setup guide has a section on per-context and per-page proxies.

For multi-step flows, keep one browser session on one proxy identity. Do not rotate between login, cart, checkout, form submission, or account pages. If your workflow needs a stable identity, read sticky vs rotating proxies before you change the code.

When better proxies help

Better proxies help when the Puppeteer configuration is correct, but the target reacts to IP reputation, region, request concentration, or unstable sessions.

Use residential proxies when you need rotating or sticky consumer routes and location targeting. Use ISP proxies when you need stable dedicated IPs, lower latency, and one proxy identity per browser profile or task.

Better proxies do not fix:

FAQ

Why does Puppeteer show my local IP instead of the proxy IP?

The proxy flag may be missing, malformed, or bypassed, or the code may launch a different browser than you think. Log the launch args, remove broad bypass rules, and visit an IP echo page through the browser before you test the target.

What does net::ERR_PROXY_CONNECTION_FAILED mean in Puppeteer?

Chrome could not resolve the proxy hostname or open a connection to the proxy port. Check the host, port, local network rules, and whether the proxy works with curl -x.

Why do I get net::ERR_TUNNEL_CONNECTION_FAILED with an authenticated proxy?

Most often the credentials are inside --proxy-server, and Chrome ignores them. Pass only http://host:port in the flag and call page.authenticate() before navigation. If that does not fix it, run curl -v -x against the same HTTPS URL to see whether the proxy refuses the CONNECT.

Why do I get net::ERR_NO_SUPPORTED_PROXIES in Puppeteer?

The --proxy-server value is malformed. Common causes are user:pass@ inside the URL, literal double quotes in the argument, or a bad scheme. Pass only scheme://host:port and send credentials with page.authenticate().

How do I fix proxy authentication in Puppeteer?

Launch with the proxy host and port, then call page.authenticate({ username, password }) before navigation on each page. If the same credentials fail in curl, fix the proxy account or format first.

Can Puppeteer use a SOCKS5 proxy with a username and password?

Not directly. Chrome supports no SOCKS5 authentication methods, and page.authenticate() only answers HTTP auth. Use the provider's HTTP endpoint, IP allowlisting, or a local forwarder such as proxy-chain.

Can a proxy fix Puppeteer 403 errors?

Sometimes, but only if IP reputation or location is the real cause. A 403 can also come from permissions, headers, cookies, browser behavior, private pages, or site policy.

Final thoughts

Puppeteer proxy errors are easier to fix when you separate the layers. Test the proxy with curl, give Chrome a clean scheme://host:port, authenticate before navigation, avoid SOCKS5 with passwords, and check an IP echo page. Only then debug target-side 403, 429, or timeout behavior.

If you use another browser tool, the same layer-by-layer approach applies to Playwright proxy setup and errors.

Technical references: Puppeteer Page.authenticate(), Puppeteer BrowserContextOptions, Chromium proxy support notes, Chromium net_error_list.h, and MDN 407 Proxy Authentication Required.

About the Author

Unknown Proxies

Proxy Infrastructure Team

Stay Unknown

High-performance dedicated proxies optimized for speed and reliability. Get uncompromising quality, 99.9% uptime, and unmatched support. Stay Unknown.

Explore Plans