Almost every proxy error report we receive contains the error text and nothing else. The text is genuinely useful and it is also the least informative part, because the same message routinely means three unrelated failures, and picking the wrong one costs an afternoon of changing settings that were never wrong.
A proxied request has three legs. Your client reaches the proxy. The proxy reaches the target. The target decides what to do with you. A failure on the first leg looks nothing like a failure on the third, the fixes have nothing in common, and status codes cross between them freely. Sort the error by leg first and the fix is usually obvious. Sort it by status code and you will guess.
Your client
leg 1 breaks: you never reached the proxy
The proxy
leg 2 breaks: the proxy could not reach upstream
The target site
leg 3 breaks: it reached you and refused
The one-minute triage
Run these three commands before changing a single setting. They take under a minute and they identify the leg without any guessing.
# 1. Does the target answer at all, with no proxy in the path?
curl -s -o /dev/null -w '%{http_code}\n' https://your-target.example
# 2. Does the proxy work, against an endpoint that always answers?
curl -s -x http://USER:PASS@HOST:PORT https://api.ipify.org?format=json
# 3. Does the proxy reach your real target?
curl -sv -o /dev/null -x http://USER:PASS@HOST:PORT https://your-target.example
Read the result as a table rather than as three separate outcomes.
| 1. Direct | 2. Proxy to neutral | 3. Proxy to target | What broke |
|---|---|---|---|
| works | fails | fails | Leg 1 or 2. Your proxy config or the proxy itself |
| works | works | fails | Leg 3. The target is refusing that IP |
| fails | works | fails | The target is down or blocking your whole network |
| works | works | works | Intermittent. Suspect a specific IP in the pool, not the setup |
Command two is the important one, and it is the one people skip. An endpoint that returns your exit IP proves the credentials, the host, the port, the protocol and the tunnel in a single request, and it tells you which address the target is actually judging. Our proxy checker does the same job in a browser and adds the ASN and anonymity grade, which command two cannot show you.
Leg 1: you never reached the proxy
These errors come from your own machine. No packet ever arrived at the proxy, so nothing about the target, the IP quality or the pool is relevant yet.
ERR_PROXY_CONNECTION_FAILED in Chrome means the browser could not open a connection to the proxy address you configured. Wrong port is the most common cause by a wide margin, followed by a proxy that is simply dead. Six causes and their fixes are in ERR_PROXY_CONNECTION_FAILED in Chrome.
The proxy server is refusing connections is the Firefox and Tor Browser wording for the same class of failure, and it also fires when the protocol is wrong: a SOCKS5 proxy configured in the HTTP fields will refuse you politely and forever. See the proxy server is refusing connections.
cURL exit code 7, "Failed to connect", is the command-line version. Connection refused means something answered the port and said no. A hang followed by a timeout usually means a firewall dropped the packet silently. Codes 7, 56 and 60 are covered in how to fix cURL proxy errors.
ECONNREFUSED and ETIMEDOUT in Node, Python or Go carry the same distinction. Refused is an answer, timed out is silence, and silence usually means a network path problem rather than a proxy problem.
The leg-1 checklist is short because the causes are mechanical: verify host and port against the dashboard, confirm the protocol matches the field you pasted it into, check whether your own network or VPN blocks outbound connections on that port, and confirm the proxy is actually alive by testing it from a different network.
Leg 2: the proxy could not do what you asked
Now the proxy is answering. Something in its own handling of the request failed.
407 Proxy Authentication Required is the flagship of this category and comes from the proxy itself. Three causes account for nearly all of them: your source IP is not on the whitelist for a provider expecting IP authentication, credentials were attached to the request but not to the CONNECT tunnel, or a special character in the password went unencoded. Full breakdown in how to fix 407.
ERR_TUNNEL_CONNECTION_FAILED means Chrome reached the proxy and the proxy refused the HTTPS tunnel. This is where people lose time by treating it as ERR_PROXY_CONNECTION_FAILED and re-checking a host and port that were correct all along. Different error, different leg, different fix.
502 Bad Gateway is ambiguous by construction, because your proxy is a gateway and the target's edge is also a gateway. Both can emit it. The fastest disambiguation is to swap in a second proxy: a 502 that follows you belongs to the target's infrastructure, one that disappears belonged to the first proxy. See how to fix 502 through a proxy.
504 Gateway Timeout is the same ambiguity with a clock attached. Time the request. A 504 that lands consistently at a round number is usually somebody's configured timeout, and which side owns that timeout is the whole question. See how to fix 504.
cURL exit 56, "Recv failure", means the connection was established and then died mid-transfer. Through a proxy that usually means an unstable exit node or an upstream that dropped the socket, and it is one of the few errors where retrying on a different IP is a legitimate first response rather than a way of avoiding the diagnosis.
Leg 3: the target reached you and said no
Everything here is a decision, not a failure. The request completed. The site looked at the IP, the headers and the fingerprint, and declined.
403 Forbidden means the target blocked you specifically. Through a proxy it is judging a different IP than your own, so the usual causes are a datacenter ASN, an address already burned by a previous tenant, or a country mismatch between the exit IP and the rest of your fingerprint. See how to fix 403 through a proxy, and how websites detect proxies for the signals underneath the decision.
429 Too Many Requests means you were identified and rate-limited, which is a compliment to your consistency. Honour any Retry-After header, add backoff, and spread the load across more exits. See 429 and proxy rotation.
401 Unauthorized is the target's authentication, not the proxy's. It is worth naming here only because 401 and 407 get confused constantly, and they belong to different legs entirely.
503 with a challenge page is usually an anti-bot product rather than an overloaded server. If the body contains a challenge, a JavaScript check or a branded interstitial, you are looking at a bot-management decision. Our guides on Cloudflare, Akamai and DataDome cover the specific vendors.
The leg-3 fix is never in your proxy configuration, and that is the most useful thing to know about this category. Changing timeouts and re-pasting credentials cannot move a decision the target already made about the IP. What moves it is a different kind of IP, a cleaner one, or a request that stops looking automated.
TLS errors, which misreport their own leg
Certificate failures deserve their own section because they routinely blame the wrong part of the path.
cURL exit 60 and the browser equivalents mean certificate verification failed. Through a proxy this can be the target's certificate, the proxy's own certificate on an HTTPS-to-proxy connection, or a middlebox on your network intercepting TLS. The three have nothing in common except the message.
Separate them by making the proxy connection itself explicit. --proxy-insecure relaxes verification of the proxy leg only, so if the error clears with it and returns without it, the certificate in question is the proxy's, not the target's. If the error survives both, the target or something between you and it owns the problem.
Never leave verification disabled as the fix. It converts a visible error into an invisible interception, which is the wrong trade in every situation including the ones where it is tempting. Our HTTP vs HTTPS proxy explainer covers where the encryption boundaries actually sit.
The failures that never raise an error
The most expensive errors in production are not errors. They return 200 and look like success to every health check you have.
200 with a block page. Anti-bot systems commonly answer with a valid page containing a challenge or an interstitial rather than an error status. Your scraper records a success, your parser finds nothing, and the metric that would have caught it is not the status code. Assert on the content: a known selector, a minimum body length, a string that only appears on the real page.
200 with the wrong country. The request succeeded and returned perfectly valid content for a country you did not want, because the exit IP geolocated somewhere other than the label on it. Price and availability scrapes silently produce wrong data this way for weeks. Verify the exit country per session, not per configuration.
200 with stale or degraded content. Some edges serve a cached or reduced version to traffic they distrust rather than blocking it. Nothing is broken. The data is just quietly not the data users see.
A health check that only reads status codes will pass through all three. One that asserts on content catches all three, and it is a ten-line change.
Intermittent failures usually mean the IP changed underneath you
A class of error report reads "it works, then it does not, then it works again", and the config is fine in all three states. On a rotating pool the usual cause is that the exit IP moved mid-flow.
Anything with server-side state hates this. A logged-in session, a multi-step checkout, a paginated result set tied to a cursor, a CSRF token issued to one address: each of those was granted to an IP, and when the next request arrives from a different one the site treats it as a hijacked session. What you see is a 401, a sudden 403, or a captcha appearing at step four of a flow that passed steps one to three, and it will not reproduce reliably because rotation is not deterministic.
Long-lived connections take this hardest of all, which is why WebSocket support through a proxy gets its own guide: a socket held for an hour cannot survive an exit that moves under it, and the close it produces carries no error worth reading.
The fix is a sticky session, which holds one exit for a set window so a flow completes on the address it started on. Our sticky versus rotating explainer covers when each is correct, and the short rule is that rotation is for breadth and stickiness is for state. If a failure only appears on multi-step work and never on single requests, check the session mode before you check anything else.
The lookup table
One row per error we see most, sorted by the leg it belongs to.
| Error | Leg | Most likely cause | Where it is covered |
|---|---|---|---|
| ERR_PROXY_CONNECTION_FAILED | 1 | Wrong port, or a dead proxy | Chrome fixes |
| The proxy server is refusing connections | 1 | Protocol pasted into the wrong field | Refusing connections |
| cURL 7, Failed to connect | 1 | Refused means answered, hang means firewalled | cURL errors |
| ECONNREFUSED / ETIMEDOUT | 1 | Refused is an answer, timeout is silence | cURL errors |
| 407 Proxy Authentication Required | 2 | Unwhitelisted source IP, or credentials missing on CONNECT | Fixing 407 |
| ERR_TUNNEL_CONNECTION_FAILED | 2 | The proxy refused the HTTPS tunnel | This page |
| 502 Bad Gateway | 2 or 3 | Two gateways can emit it; swap proxies to tell | Fixing 502 |
| 504 Gateway Timeout | 2 or 3 | Somebody's configured timeout; time the request | Fixing 504 |
| cURL 56, Recv failure | 2 | Unstable exit, connection died mid-transfer | cURL errors |
| 403 Forbidden | 3 | Datacenter ASN or a burned address | Fixing 403 |
| 429 Too Many Requests | 3 | Identified and rate-limited | 429 and rotation |
| 503 with a challenge body | 3 | Bot management, not an overloaded server | Past Cloudflare |
| cURL 60, certificate problem | mixed | Target cert, proxy cert, or interception | cURL errors |
| 200 with no usable content | 3 | A block page is a valid page | This page |
| Intermittent 401 or captcha mid-flow | 3 | The exit IP rotated during a stateful flow | Sticky vs rotating |
When it is the pool, not the configuration
If triage keeps landing on leg 3, the setup is not the problem and no amount of configuration will fix it. At that point the questions are about the IPs themselves: what ASN they announce, whether the address is shared with traffic you cannot see, and what reputation the previous tenant left behind. That is a supply question, and it is the reason free proxies produce so many leg-3 errors: our own engine has run 129,131,311 checks across 589,918 discovered proxies, and only 4,023 were answering at the last measurement.
You can test that claim without paying anything. Take any IP, from us or anyone else, and run it through the proxy checker to see the real exit country, the ASN and the anonymity grade the target will see. When a job needs IPs that stop generating leg-3 errors in the first place, our residential proxies start at $0.50/GB for a single gigabyte, pay as you go, with a balance that does not expire.