Proxy error codes are HTTP status codes and client errors that come from one of two places: the proxy server you connect through, or the website behind it. 407 always comes from the proxy. 403, 429 and 503 usually come from the target site. 502 and 504 mean the proxy could not get a good answer from the target in time. To tell them apart, run the request with curl -v: an error in reply to the CONNECT line is the proxy's; an error after CONNECT tunnel established is the website's.
That one distinction decides the fix. Changing credentials will not cure a rate limit, and slowing down will not cure a typo in a port number. The table below is the quick lookup; the sections after it take each proxy error code in turn, with the commands to confirm and fix it.
Proxy error codes: quick lookup table
| Code or message | Usually sent by | What it means | First thing to try |
|---|---|---|---|
| 407 Proxy Authentication Required | Proxy | Missing or wrong proxy credentials | Recopy username and password from the order; URL-encode special characters |
| 403 Forbidden | Target (sometimes proxy) | Request refused: IP reputation, fingerprint, geography, or a proxy access rule | Compare with and without the proxy; check headers and IP type |
| 429 Too Many Requests | Target | You are over the site's rate limit | Back off, honour Retry-After, lower concurrency per IP |
| 502 Bad Gateway | Proxy | The proxy got no valid answer upstream (DNS failure, refused connection) | Check the target hostname resolves; retry on another IP |
| 503 Service Unavailable | Target (sometimes proxy) | Target overloaded or challenging you; or the proxy has no capacity | Look at the body: a challenge page means a block, not an outage |
| 504 Gateway Timeout | Proxy | The target took too long to answer the proxy | Raise the read timeout; test the target directly |
ERR_PROXY_CONNECTION_FAILED | Browser | The browser cannot reach the proxy at all | Check host and port with curl -v or nc -vz |
CONNECT tunnel failed, response N | Proxy | The proxy refused to open an HTTPS tunnel; N is its reason | Read N and jump to that code's section |
| SSL/TLS errors | Your client or the target | Wrong proxy scheme, bad target certificate, or TLS interception | Use http:// for the proxy URL; inspect the certificate chain |
How to tell if the proxy or the target sent the error
For HTTPS targets (nearly all of them today), your client first asks the proxy to open a tunnel with an HTTP CONNECT request. The proxy answers that request itself. Once it says 200 Connection established, everything that follows is encrypted end to end between you and the website, and the proxy can no longer read or rewrite it. So the position of the error in the exchange tells you who sent it.
Here is a 407 from a local test proxy, run with bad credentials:
curl -v -x http://USERNAME:WRONG@HOST:PORT https://httpbin.org/ip
> CONNECT httpbin.org:443 HTTP/1.1
< HTTP/1.1 407 Proxy Authentication Required
< Proxy-Authenticate: Basic
< Connection: close
* CONNECT tunnel failed, response 407
The 407 is the reply to CONNECT, and it carries a Proxy-Authenticate header, which only a proxy sends. Compare a 429 through the same proxy with correct credentials:
> CONNECT httpbin.org:443 HTTP/1.1
< HTTP/1.1 200 Connection established
* CONNECT tunnel established, response 200
< HTTP/2 429
< server: gunicorn/19.9.0
The tunnel opened, then the target's own server (note its server header) answered 429. Nothing about the proxy needs fixing here. Three signals help:
- Position. Reply to
CONNECTmeans proxy. After the tunnel means target. - Headers.
Proxy-Authenticate, and often aViaor proxy-software header, point at the proxy. The target'sServerheader and cookies point at the site. Header names vary by proxy software, so treat them as hints. - Plain HTTP targets. For
http://URLs there is no tunnel: the proxy forwards the request and could return either its own error or the target's. Headers are your only clue there, which is one more reason to test against an HTTPS endpoint.
In Python, requests makes the same split for you. A refused CONNECT raises requests.exceptions.ProxyError with the proxy's status in the message (Tunnel connection failed: 407 Proxy Authentication Required), while a target's error comes back as an ordinary response with a status code. This small script classifies a URL; we ran it against a local authenticating proxy for every case in this article:
import os
import sys
import requests
PROXY = os.environ["PROXY_URL"]
def diagnose(url):
try:
resp = requests.get(url, proxies={"http": PROXY, "https": PROXY}, timeout=(5, 20))
except requests.exceptions.ProxyError as exc:
return f"PROXY: {exc}"
except requests.exceptions.SSLError as exc:
return f"TLS: {exc}"
except requests.exceptions.ConnectTimeout:
return "TIMEOUT while connecting"
except requests.exceptions.ReadTimeout:
return "TIMEOUT waiting for the response"
if resp.status_code == 407:
return f"PROXY: 407, Proxy-Authenticate={resp.headers.get('Proxy-Authenticate')}"
return f"TARGET: {resp.status_code} (Server: {resp.headers.get('Server')})"
if __name__ == "__main__":
for url in sys.argv[1:]:
print(url, "->", diagnose(url))
export PROXY_URL="http://USERNAME:PASSWORD@HOST:PORT"
python diagnose.py https://httpbin.org/status/429 https://api.ipify.org?format=json
Copy HOST, PORT, USERNAME and PASSWORD from your order in the dashboard. On ProxyHive, each ISP or datacenter IP is its own endpoint with its own ports, as described in the docs.
407 Proxy Authentication Required
Sent by: the proxy, always. RFC 9110 defines it as the proxy's version of 401: you must authenticate with the proxy before it forwards anything.
Likely causes:
- A wrong or stale username or password, often from copying the wrong order.
- Special characters in the password (
@,:,#,/,%) that break the proxy URL because they were not URL-encoded. - The order is set to IP allowlist authentication and your public IP has changed (home connections and CI runners change IPs often).
- The client never sends credentials for HTTPS. Chrome, for example, ignores credentials in
--proxy-serverand asks in a dialog instead.
Fixes:
Pass the credentials separately so nothing needs encoding, and confirm the proxy accepts them:
curl -v -x http://HOST:PORT -U "USERNAME:PASSWORD" https://api.ipify.org?format=json
If that works but your code does not, encode the credentials when you build the URL:
from urllib.parse import quote
proxy = f"http://{quote(USERNAME, safe='')}:{quote(PASSWORD, safe='')}@{HOST}:{PORT}"
If the order uses an allowlist, check your current address with curl https://api.ipify.org (without the proxy) and compare it with the allowlist on the order.
403 Forbidden through a proxy
Sent by: usually the target; occasionally the proxy.
A 403 after the tunnel opened is the website refusing you. Common reasons are the reputation of the IP range (datacenter ranges are the first to be refused by strict sites), a request fingerprint that does not look like a browser, a geographic restriction, or a login wall. A 403 in reply to CONNECT is the proxy's own access rule: many proxies refuse tunnels to ports other than 443, or to destinations their policy blocks.
How to confirm: run the same request without the proxy and through it. If both fail, the proxy is not the problem. If only the proxied request fails, check what the site sees:
curl -x http://USERNAME:PASSWORD@HOST:PORT https://api.ipify.org?format=json
curl -x http://USERNAME:PASSWORD@HOST:PORT https://httpbin.org/headers
Fixes: send realistic headers, slow down, and match the IP type to the target: an ISP IP registered to a consumer carrier is refused less often than a datacenter IP on sites that check the network type. Our guide on how to avoid getting blocked while scraping covers headers, fingerprints and pacing in depth. If the target forbids automated access in its terms, a different IP will not change that, and our allowed-use policy sets out what we permit.
429 Too Many Requests
Sent by: the target, almost always. Defined in RFC 6585, it means you sent more requests than the site allows in a window. The response may include a Retry-After header, either in seconds or as a date.
Likely causes: too many requests per second, too many concurrent connections from one IP, or a burst at startup when every worker fires at once.
Fixes:
- Honour
Retry-Afterwhen present. Otherwise back off exponentially with random jitter. - Cap concurrency per IP, not only overall.
- Spread load across more IPs only after pacing is sane. Rotating through ten IPs at the same aggressive rate gets ten IPs rate limited.
Some proxy providers also answer CONNECT with 429 when you exceed their own connection limits. The position test above tells you which limit you hit. If you run a list of static IPs, rotating proxies in Python shows how to spread requests across them and bench the ones that keep failing.
502 Bad Gateway from a proxy
Sent by: the proxy. It tried to reach the target and got nothing usable: the hostname did not resolve, the target refused the connection, or the upstream connection dropped mid-response.
Here is a real 502 from a local proxy asked to tunnel to a hostname that does not exist:
* Establish HTTP proxy tunnel to no-such-host.invalid:443
< HTTP/1.1 502 Bad Gateway
* CONNECT tunnel failed, response 502
Fixes:
- Check the hostname for typos and confirm it resolves:
dig +short example.com. - Request the target without the proxy. If it is down for everyone, wait.
- Retry through a different IP. A 502 that follows one exit IP and not others points at that path.
- If you use SOCKS5, remember the difference between
socks5://(your machine resolves DNS) andsocks5h://(the proxy resolves it). Our HTTP vs SOCKS5 comparison explains when that matters.
503 Service Unavailable
Sent by: usually the target, sometimes the proxy.
From the target, a 503 means it is overloaded, in maintenance, or, very often, serving a bot challenge with a 503 status. Read the body before you conclude the site is down: a page mentioning a browser check or a CAPTCHA is a block, and the fix is the same as for 403. From the proxy, a 503 in reply to CONNECT means it had no capacity or no upstream route for that request.
Fixes: save the response body and look at it; retry later with backoff for real outages; treat challenge pages as blocks.
curl -s -o body.html -w "%{http_code}\n" -x http://USERNAME:PASSWORD@HOST:PORT https://example.com/
grep -iE "captcha|challenge|unusual traffic" body.html
504 Gateway Timeout
Sent by: the proxy. It connected to the target, or tried to, and gave up waiting before an answer arrived.
Likely causes: a slow target (heavy search pages, reports), a target far from the proxy's location, or a target that slow-walks suspected bots instead of blocking them.
Fixes: time the target directly with curl -w "%{time_total}\n" -o /dev/null -s https://example.com/; raise your client's read timeout for slow pages while keeping the connect timeout short; choose an IP closer to the target, which static ISP and datacenter IPs let you do by country and city.
ERR_PROXY_CONNECTION_FAILED and other browser errors
ERR_PROXY_CONNECTION_FAILED is Chrome's message for "I could not connect to the proxy server". No request reached any website. Its cousin ERR_TUNNEL_CONNECTION_FAILED means Chrome reached the proxy but the CONNECT was refused, which usually hides a 407, 403 or 502.
Likely causes: a wrong host or port, a proxy port blocked by a corporate firewall, an IP allowlist that does not include your address, or a stale system proxy left behind by a VPN client or an extension you removed.
Fixes:
nc -vz HOST PORT
curl -v -x http://USERNAME:PASSWORD@HOST:PORT https://api.ipify.org?format=json
If nc cannot connect, no browser setting will help: check the host, port and your network. If curl works and the browser does not, the problem is the browser's proxy configuration. On Windows, netsh winhttp show proxy shows the system-level WinHTTP proxy. Our setup guides for the Windows proxy settings and SwitchyOmega in Chrome walk through each screen.
SSL errors through a proxy
TLS errors through a proxy come in three flavours, and turning off certificate verification hides all three.
1. The proxy URL uses the wrong scheme. Writing https://HOST:PORT for a proxy that listens for plain HTTP makes your client try TLS with the proxy itself. curl reports (35) SSL_connect errors or "wrong version number"; Python may hang or raise a ProxyError. The fix is http:// in the proxy URL, even for HTTPS targets: the target's TLS runs inside the tunnel regardless.
2. The target's certificate is bad. curl's (60) SSL certificate problem: certificate has expired came from the target, through a working tunnel. Test without the proxy to confirm; it is not the proxy's fault.
3. Something intercepts TLS. Corporate proxies and some antivirus tools decrypt traffic and re-sign it with their own certificate authority. Check who issued the certificate you receive:
openssl s_client -proxy HOST:PORT -proxy_user USERNAME -proxy_pass pass:PASSWORD \
-connect example.com:443 -servername example.com </dev/null 2>/dev/null | grep -E "subject=|issuer="
If the issuer is not a public certificate authority you recognise, the path is intercepted. Install that authority where your client trusts it, or route around the interception.
CONNECT tunnel failed
curl: (56) CONNECT tunnel failed, response 407 is curl telling you the proxy refused the tunnel, and the number is the proxy's status code. Read it and go to that section: 407 for credentials, 403 for a proxy rule (often a non-443 port), 502 for an unreachable target, 503 for capacity.
The other curl exit codes you will meet around proxies, all reproduced in our tests, are listed in the libcurl error reference:
| curl exit | Message | Meaning |
|---|---|---|
| 5 | Could not resolve proxy | The proxy hostname does not resolve: typo or DNS problem |
| 7 | Failed to connect ... Couldn't connect to server | Nothing listens on that host and port, or a firewall refuses it |
| 28 | Timeout was reached | Packets are dropped on the way to the proxy, or the target is slow |
| 35 | SSL_connect error | Usually https:// in the proxy URL for a plain HTTP proxy |
| 56 | CONNECT tunnel failed, response N | The proxy refused the tunnel with status N |
| 60 | SSL certificate problem | The target's certificate failed verification |
Before you contact support
When the error is on the proxy side, a good report gets a fast answer. Collect:
- The full
curl -voutput for one failing request, with the password removed. - The time of the failure, with timezone.
- The target hostname and whether it fails without the proxy.
- The IP echo result:
curl -x ... https://api.ipify.org?format=json. - Whether every IP on the order fails or only one.
With that, a person on our support rotation can tell you in one reply whether the problem is the order, the network or the target. The curl setup guide has the flags used throughout this article.