Originally published on proxymint.
A proxy timeout names the phase that stalled: connect, the CONNECT tunnel, TLS or first byte. Read the error, time each phase with curl, fix the right side.
TL;DR
- A proxy timeout names the phase that stalled. A proxied HTTPS request has four phases: TCP to the proxy, the CONNECT tunnel, TLS with the target and the first byte. Each one stalls for different reasons.
- One curl command shows which phase stalled. time_connect, http_connect, time_appconnect and time_starttransfer split the request; an http_connect of 200 means the proxy opened the tunnel.
- Clients file the same stall under different names. In a 2026-10-07 lab run, requests reported a proxy stuck on CONNECT as a ReadTimeout against the target host, httpx as a ReadTimeout after its read timer, and curl as "Proxy CONNECT aborted due to timeout".
- A 504 is a status, so a server sent it. A 504 in answer to CONNECT came from the proxy; a 504 inside a completed tunnel came from the site's own gateway.
Key takeaways
- Split connect from read and add a total deadline. A short connect timeout, a read timeout sized to the target and a cap on the whole request cover nearly every client.
- Catch ProxyError as well as Timeout in requests. An unreachable proxy raises ProxyError, which is not a subclass of requests.exceptions.Timeout.
- Retry by phase. Retry a connect timeout once on a fresh connection, stop on a refused port, back off on CONNECT stalls, 502s and 504s, slow down on read timeouts, and never retry a 407.
- Raise a timeout only for a target that is slow by design. A longer timer never fixes a wrong port, a blocked egress or a proxy that already answered 504.
Read the full guide on proxymint, with the commands, tables and sources.
Top comments (0)