Back to all posts
How to Diagnose HTTP 502 Errors Behind a Forward Proxy

How to Diagnose HTTP 502 Errors Behind a Forward Proxy

October 22, 2026

What a 502 means in a proxied request

A forward proxy sits between your application and the destination server. A 502 Bad Gateway means that an intermediary could not obtain a valid response from the next hop, but it does not reveal which component failed.

For HTTPS, the usual request path is:

Client -> forward proxy -> TCP connection -> destination
                                  |
                                  +-> TLS handshake
                                  +-> HTTP request and response

The client first sends an HTTP CONNECT request to the proxy. If the proxy returns 200 Connection Established, it creates a tunnel. The client then performs the TLS handshake and sends the actual HTTPS request through that tunnel.

That creates two important possibilities:

  • The proxy can return 502 when it cannot connect to, resolve, or reach the destination.
  • The destination's reverse proxy, CDN, or application can return 502 after the tunnel and TLS handshake have succeeded.

In a normal, non-intercepting forward proxy, both errors look identical to an application receiving a 502 response.

Capture the complete proxy path

Start with one reproducible request and save its headers and body:

export PROXY_URL='http://proxy.example.net:8080'
export PROXY_USER='your-user'
export PROXY_PASS='your-password'

curl --verbose --show-error \
  --proxy "$PROXY_URL" \
  --proxy-user "$PROXY_USER:$PROXY_PASS" \
  --connect-timeout 10 \
  --max-time 30 \
  --dump-header /tmp/proxy-headers.txt \
  --output /tmp/proxy-body.txt \
  https://api.example.com/health

Do not add --insecure during the first diagnosis. Disabling certificate verification can hide a TLS problem and makes the result less useful.

Read the verbose output in chronological order:

  • CONNECT tunnel failed, response 502 means the proxy answered the CONNECT request with an error. The proxy could not create a usable upstream connection.
  • CONNECT tunnel established followed by a certificate or protocol error means the tunnel works, but the destination's TLS handshake failed.
  • CONNECT tunnel established, followed by a successful TLS handshake and then HTTP/1.1 502 or HTTP/2 502, means the destination side returned the final response.
  • A connection refusal or timeout before any HTTP status usually means the client could not reach the proxy itself, not the destination.

For plain HTTP, there is normally no CONNECT tunnel. The proxy receives an absolute-form request such as GET http://api.example.com/health HTTP/1.1. A 502 is therefore ambiguous: it may come from the proxy or from the destination.

Evidence Failing stage Most likely owner
Connection refused before HTTP Client to proxy Proxy endpoint
407 Proxy Authentication Required Proxy authentication Credentials or account
CONNECT receives 502 Proxy to destination Provider, DNS, routing, or destination refusal
CONNECT succeeds, TLS fails Destination TLS SNI, certificate, clock, or protocol
CONNECT and TLS succeed, final response is 502 Destination HTTP stack CDN, reverse proxy, or application
Final 502 over plain HTTP Ambiguous Repeat with another exit or HTTPS route

Isolate the failure with controlled comparisons

Use comparisons that change one variable at a time:

  1. Verify the proxy route. Send a request through the proxy to the provider's exit-IP endpoint or another lightweight IP-check service. This confirms authentication, gateway reachability, and egress selection, although it does not prove that the target is reachable.

  2. Compare with a direct request when policy permits it. If the same URL fails directly, the likely fault is the target, CDN, or local DNS. If it works directly but fails through the proxy, investigate the proxy path or egress reputation. A direct request may reach a different DNS or CDN edge, so treat this as evidence rather than proof.

  3. Try a second exit IP. Use a fixed or sticky session if the provider supports one. If only one exit fails, quarantine that pool member. If every exit fails, investigate provider routing, DNS, or the destination itself.

  4. Test another path on the same host. Compare /health with the failing API or feature endpoint. A healthy health endpoint suggests that the application is running but a specific dependency, upstream service, or route is broken.

  5. Check provider logs against your UTC timestamp. For CONNECT requests, the proxy normally resolves the destination hostname. A successful local dig command therefore does not prove that the proxy's resolver can reach the same address.

Distinguish proxy failures in Python

With requests and an HTTPS URL, a non-successful CONNECT response is normally raised as a ProxyError. A returned response means the tunnel was established and the destination's HTTP response was received.

import os
import requests
from requests.exceptions import ConnectTimeout, ProxyError, RequestException

target = 'https://api.example.com/health'
proxy_url = os.environ['PROXY_URL']  # May contain credentials; never print it.
proxies = {'http': proxy_url, 'https': proxy_url}

session = requests.Session()
# Prevent ambient HTTP_PROXY or HTTPS_PROXY variables from changing the test.
session.trust_env = False

try:
    response = session.get(
        target,
        proxies=proxies,
        timeout=(5, 20),
        allow_redirects=False,
        headers={'User-Agent': 'proxy-diagnostic/1.0'},
    )
except ProxyError:
    print('Failure stage: proxy connection or CONNECT tunnel')
except ConnectTimeout:
    print('Failure stage: connection timeout')
except RequestException as exc:
    print('Transport failure:', type(exc).__name__)
else:
    print('HTTP response received:', response.status_code)
    print('Server:', response.headers.get('server'))
    print('Via:', response.headers.get('via'))

This distinction is specific to an HTTPS URL. For a plain HTTP target, a proxy-generated 502 may appear as an ordinary response.status_code == 502.

Retry according to the failing stage

Blindly rotating IPs after every 502 can turn a small origin incident into a larger one. Apply a stage-aware policy:

  • CONNECT returns 502: Treat the egress as unhealthy. Retry through a different exit with a small attempt limit. If all exits fail, stop and contact the provider.
  • The destination returns 502 after a successful tunnel: Use bounded exponential backoff with jitter, normally no more than three attempts. Rotate only if evidence points to a region-specific CDN or routing problem.
  • The request is non-idempotent: Do not retry a POST, order, or payment blindly. The origin may have processed it before the response failed. Retry only when the API supports an idempotency key or another deduplication mechanism.
  • The response includes Retry-After: Honor it when present; otherwise use a delay such as min(30, 2 ** attempt) seconds plus random jitter.
  • The target is consistently unhealthy across exits: Continue rotating only if your service-level objective requires it. Otherwise, fail gracefully and alert the owning team.

Fix the component that actually failed

Use the classification to choose the remedy:

  • Bad pool member: Ask the provider to remove or replace it, or route around it.
  • Proxy policy rejection: Verify allowed destinations, ports, plans, and organization allowlists with the provider.
  • Proxy DNS or egress failure: Provide the timestamp, destination hostname, proxy region, and egress IP to provider support.
  • TLS failure after CONNECT: Check SNI, certificate names, supported TLS versions, and clock synchronization.
  • Destination-generated 502: Inspect application logs, upstream dependencies, recent deployments, and CDN or API-gateway request IDs.

Avoid misleading fixes. --insecure does not repair a CONNECT failure, changing the User-Agent cannot affect a connection that failed before HTTP, and checking local DNS may test the wrong resolver.

Build a safe escalation packet

Give the target owner or proxy provider:

  • The UTC timestamp, request method, target hostname, and redacted path
  • The proxy endpoint, session identifier, egress IP, and provider region
  • The cURL version and command with credentials removed
  • Whether CONNECT and TLS succeeded
  • The final status, selected response headers, and a short redacted body sample
  • Results from a direct request and a second exit, when available
  • Application, CDN, or gateway correlation IDs

Verbose cURL logs can expose Proxy-Authorization, cookies, tokens, and request bodies. Treat those logs as sensitive and redact them before sharing.

A reliable diagnosis always answers three questions in order: Did the proxy authenticate, did CONNECT succeed, and did TLS succeed? The first point where the trace fails identifies where troubleshooting should continue.