Proxy timeout? Find which operation is waiting
Diagnose proxy connection and read timeouts with bounded curl checks. Compare HTTPX and Requests limits, then separate client waits from gateway errors.
A proxy timeout means an operation exceeded a configured time limit. Identify whether the client was connecting, waiting for response data or waiting for a free connection. Increasing every timeout can hide the failure and leave more requests waiting.
Identify the operation that timed out
Keep the exact exception name and the timeout settings. HTTPX distinguishes connect, read, write and pool timeouts. A pool timeout means the client waited for an available connection; it is not direct evidence that a remote proxy stopped responding.
Requests supports separate connect and read timeout values. Its timeout is not a deadline for downloading the entire response. A server that keeps sending data can keep the request alive beyond that value. Use your job runner's deadline when you need a wall-clock limit for the whole task.
| Observation | Next check |
|---|---|
| Connect timeout | Endpoint, protocol, network route and current rental state. |
| Read timeout | Response progress from the proxy or destination. |
| Pool timeout | Client connection limits and responses left open. |
| HTTP 504 response | Which gateway returned it and what it was waiting for. |
References: Requests timeout semantics; HTTPX timeout types
Use a small, bounded request
Use the private curl configuration from the authentication guide with a 10-second connect timeout and a 30-second overall limit. These are example diagnostic limits, not promised service timings. Adjust them to a measured workload once you know where time is spent.
Compare the same small URL directly and through the proxy from the same machine, where direct testing is permitted. Do not run a large parallel speed test while diagnosing one failed connection.
curl -q --config "$HOME/.config/proxy-check.curl" \
--connect-timeout 10 --max-time 30 \
--output /dev/null \
--write-out 'connect_elapsed=%{time_connect}s first_byte=%{time_starttransfer}s total=%{time_total}s http=%{http_code}\n' \
https://example.com
printf 'curl_exit=%s\n' "$?"References: curl proxy and configuration options
Read curl's timing fields
curl exit code 28 reports that a time limit was reached. The elapsed-time fields narrow the investigation, but they cannot independently locate delay inside a carrier network or destination server. Time to first byte includes the work performed before the first response byte arrives.
time_connect is elapsed time from the transfer start until the connection completes, including earlier name resolution. It is not the duration of the TCP handshake alone. For a proxy request, that connection is to the proxy; later tunnel and destination work still has to finish.
If curl reaches its deadline before receiving a response byte, the first-byte field may stay at zero. Include that run as a failed sample. Reporting only completed requests makes an unreliable connection look faster than it is.
References: curl proxy and configuration options
HTTP 200 can arrive before a timeout
Our local fixture sent HTTP 200 headers, then delayed the response body. curl 8.7.1 reached its 0.25-second total limit and exited with code 28 while still reporting HTTP 200. Checking only the status would have counted that incomplete response as a success.
Requests 2.32.5 completed a six-byte response in about 0.92 seconds with a 0.4-second read timeout because each byte arrived about 0.15 seconds apart. A separate fixture that paused the body beyond a 0.25-second read timeout raised ConnectionError with a read-timeout cause. This was the observed exception wrapper in this version.
| Controlled response | Observed result |
|---|---|
| Headers, then delayed body; curl max-time 0.25 | HTTP 200; exit 28 after about 0.25 seconds. |
| One byte every 0.15 seconds; Requests read timeout 0.4 | Completed after about 0.92 seconds. |
| Headers, then delayed body; Requests read timeout 0.25 | ConnectionError after about 0.25 seconds. |
python3 proxy-diagnostics-lab.py --output proxy-lab-results.jsonRecorded on macOS with Python 3.9.6 on 13 November 2026. These local timings demonstrate timeout semantics, not proxy speed. A heavily stalled machine can change the timing results.
Change one condition at a time
- Keep the endpoint, URL and client constant and repeat a small number of checks. Record failures as well as successes.
- Compare a second small destination you are authorized to request. A failure on one destination needs a different investigation from a failure on every destination.
- Reduce concurrency to one request. If that helps, inspect client pooling and workload capacity before buying more ports.
- Check whether a rotation or carrier reconnect coincided with the failure. Keep long-running work separate from deliberate rotation where possible.
Retry only when the operation permits it
A timed-out request may still have reached the destination. Do not automatically repeat purchases, messages or other state-changing actions without an application-specific idempotency mechanism. For safe read checks, use a limited retry count, a delay and an overall deadline.
If the proxy works on neutral requests but the application still times out, send the application error and a redacted timing comparison to support. Include whether the same timeout occurs with one request and a second destination.