Proxy Troubleshooting by Error Code and Symptom

Proxy 502 Bad Gateway and Which Side Sent It

A 502 can come from the proxy before the tunnel opens or from the website after it. Tell them apart in curl, Python and Node, then fix the right side.

By Proxies.sx Team. Reviewed .

If the 502 is the proxy's answer to CONNECT, before any TLS handshake, the proxy refused and its body names the reason. If the 502 arrives after the TLS handshake, the website or its CDN sent it through a working tunnel. Retry the first kind with different filters or after a pause. Slow down for the second kind.

In this guide

Two different 502 responses

RFC 9110 defines 502 as a response from a server acting as a gateway or proxy that "received an invalid response from an inbound server" (section 15.6.3). With a proxy in the path, two servers can act as a gateway: our proxy, and whatever load balancer or CDN sits in front of the website.

For an HTTPS URL, the client opens a tunnel with CONNECT. The proxy's answer to CONNECT arrives before the TLS handshake. Once the proxy answers 2xx it switches to forwarding encrypted bytes in both directions (section 9.3.6), and from then on it cannot write a status line into the stream. So a 502 that you can read after the handshake was produced on the website's side.

When the 502 arrivesWho sent itBody
As the answer to CONNECT, before TLSThe proxyOn our gateway, a code such as E_NO_STOCK_COUNTRY and a req id
After the TLS handshake, as the page responseThe website, its CDN or load balancerWhatever the site returns, often an HTML error page
On a plain http:// requestEitherCheck the body for an E_ code to tell which

Tell them apart with curl

curl's %{http_connect} variable holds the proxy's answer to CONNECT and %{http_code} holds the website's status (curl manual). One line of output answers the question.

We checked both cases on 19 October 2026 with local test servers and curl 8.7.1. A proxy refusing CONNECT gave connect=502 code=000. A proxy that opened the tunnel to an HTTPS server answering 502 gave connect=200 code=502.

curl -sS -o /dev/null --max-time 30 \
  -x http://gw.proxies.sx:7000 \
  -U 'psx_ab12cd-peer-br:YOUR_PROXY_PASSWORD' \
  -w 'connect=%{http_connect} code=%{http_code}\n' \
  https://example.com/

# connect=502 code=000  -> the proxy refused; read its code
# connect=200 code=502  -> the website side returned 502

The same split in Python and Node

HTTP libraries usually raise an exception for a refused tunnel and return a normal response object for the website's 502. With Requests 2.32.5 and urllib3 2.6.3, the refusal raised requests.exceptions.ProxyError whose text ends in OSError('Tunnel connection failed: 502 Bad Gateway'). The website's 502 came back as a response with status_code == 502. Requests documents its proxy settings in the advanced usage guide.

Node 24's built-in fetch, with NODE_USE_ENV_PROXY=1 and HTTPS_PROXY set, gave less to work with in the same local test: it rejected with fetch failed and the cause Request was cancelled. In Node, run the curl check above to read the status. The Node fetch proxy guide covers that setup.

import time
from urllib.parse import quote

import requests

password = quote("YOUR_PROXY_PASSWORD", safe="")
proxy = f"http://psx_ab12cd-peer-br:{password}@gw.proxies.sx:7000"
proxies = {"http": proxy, "https": proxy}

def fetch(url, attempts=3):
    for attempt in range(attempts):
        try:
            response = requests.get(url, proxies=proxies, timeout=30)
        except requests.exceptions.ProxyError:
            # The gateway refused the tunnel. Retry a few times with a pause.
            if attempt == attempts - 1:
                raise
            time.sleep(2 ** attempt)
            continue
        if response.status_code in (502, 503, 504):
            # The website side answered. Do not loop on it.
            print("website returned", response.status_code)
        return response

fetch("https://example.com/")

Fix a 502 from the gateway

Read the code first. The error code reference has the full list.

E_NO_STOCK_COUNTRY means nothing currently matches your pool, country and filters. Check the country's count in the stock endpoint. The mobile pool (mbl) exists in six countries, so a username such as psx_ab12cd-mbl-de always fails. Use the residential pool (peer-de) for Germany. Carrier and city filters narrow the candidates further. The username guide shows how each token changes selection.

E_STOCK_DEGRADED means an endpoint went away during setup. Retry after a second or two. A sticky session picks a replacement in the same country by default.

A 590 to 599 status means the upstream device or relay refused or dropped the connection. One retry on a new connection is reasonable. If the status repeats for one session id, start a new session id. If the status is 593, the target hostname failed to resolve, so check the hostname.

The gateway sends 503 with E_INTERNAL when an internal step fails, and a plain 503 with Gateway restarting; reconnect shortly while a process restarts. Both clear on retry. A 504 from the gateway means the upstream connection timed out (RFC 9110, section 15.6.5).

The website can send 503 and 504 too, through an open tunnel. A 503 from a site may carry a Retry-After header (RFC 9110, section 10.2.3). Honor it. The Retry-After guide has a Python implementation.

Browsers report a gateway refusal as ERR_TUNNEL_CONNECTION_FAILED and show no status at all. The tunnel failure guide covers that case.

When to stop retrying

Retrying a website's 502 in a tight loop multiplies traffic and gives the same answer. Check the site without the proxy, from your own connection. If it fails there too, the site has an outage. If it works directly and fails through several countries, slow your request rate and compare responses. The proxy suitability checklist describes a small test before scaling up.

Gateway refusals behave differently: a new connection can succeed a second later because selection runs again. Keep a retry cap anyway, and log the req id from the body of the last failure.

Common questions

Will a different proxy fix a website's 502?

The cause sits on the website's side when the 502 arrives after the TLS handshake. A different exit can get a different answer from the site's edge servers, but switching proxies does not repair the site.

Why do I see 590 or 599 instead of 502?

Those statuses come from proxy-chain, the library our gateway uses. They report which upstream step failed, such as 594 for a refused connection. Treat them like a 502 from the gateway.

Sources and test scope

Standards and vendor documentation checked on 19 October 2026. Gateway responses on these pages were observed with test requests or read from the gateway build running that day, and the client examples were run against local test servers. They do not establish current stock in a country or how a website will respond.

Connect your application to Proxies.sx

Use the username builder for gateway routing fields and the format converter for existing connection lists. Review Pool Gateway documentation, then check current mobile stock before buying traffic.

For agent-driven workflows, the agent service index lists the supported APIs. A configured HTTP client still needs to validate its task's response.

Proxy error and troubleshooting guides

Start from the symptom table, or open the guide for the error you saw.