curl Error: '(28) Operation timed out' — Cause, Fix, and Troubleshooting Guide
Fix curl: (28) Operation timed out after 30001 milliseconds with 0 bytes received — firewall DROP/blackhole, slow endpoints, missing routes, and low timeouts.
- #automation
- #troubleshooting
- #curl
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
curl exit code 28 means an operation exceeded its time budget. Unlike a refused connection (code 7, an instant RST), a timeout is silence: curl sent packets and got nothing back until the clock ran out. The message tells you how long it waited and whether any data arrived:
$ curl -v --max-time 30 https://api.example.com/health
* Trying 10.0.6.14:443...
* Connection timed out after 30001 milliseconds
curl: (28) Operation timed out after 30001 milliseconds with 0 bytes received
with 0 bytes received and a timeout during Trying <ip>... points at the connect phase — packets are being dropped (a firewall DROP or a blackhole route), so the handshake never completes. If bytes were received before the timeout, the connection worked but the transfer stalled — a slow or overloaded server. Distinguishing connect-timeout from transfer-timeout is the whole game.
Symptoms
curl: (28) Operation timed out after N millisecondsorConnection timed out after N ms.- curl hangs for the full timeout window before failing — not instant like a refusal.
with 0 bytes received= stuck on connect; a partial byte count = stalled transfer.nc -zvalso hangs and times out rather than reporting “refused.”- Intermittent under load, or only from certain networks (a path/route problem).
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 30 https://api.example.com/health; echo "exit: $?"
exit: 28
Common Root Causes
1. Firewall DROP or blackhole route
A firewall configured to DROP (silently discard) rather than REJECT, or a missing/black-hole route, swallows the SYN packets. curl waits with no response.
2. Overloaded or slow endpoint
The server accepts the connection but is too busy to respond within the window — saturated CPU, exhausted thread pool, or a slow downstream dependency.
3. Timeout set too low for the work
--connect-timeout or --max-time shorter than the endpoint’s normal latency turns a slow-but-working call into a failure.
4. Missing or wrong network route
No route to the destination subnet (misconfigured VPC route table, missing VPN route) drops packets into a hole.
5. Slow DNS pushing past the deadline
Resolution that takes seconds can consume the whole --max-time budget before the connection is even attempted.
6. MTU / path-MTU black hole
Large packets silently dropped when path MTU discovery is broken — the connection establishes but transfers hang.
How to Diagnose
Step 1: Separate connect timeout from transfer timeout with timing
curl -v -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
--max-time 30 https://api.example.com/health
dns=0.004 connect=0.000 ttfb=0.000 total=30.001
connect=0.000 that never advances means the TCP handshake never finished — a connect-phase (packet-drop) timeout.
Step 2: Classify with nc — hang vs refused
nc -zv -w5 10.0.6.14 443
nc: connect to 10.0.6.14 port 443 (tcp) timed out: Operation now in progress
A hang-then-timeout (not “refused”) confirms packets are being dropped, not rejected.
Step 3: Use a short connect-timeout to fail fast while testing
curl -v --connect-timeout 5 --max-time 10 https://api.example.com/health
* Connection timed out after 5001 milliseconds
curl: (28) Operation timed out after 5001 milliseconds with 0 bytes received
Step 4: Trace the network path
traceroute -T -p 443 10.0.6.14
1 10.0.0.1 0.4 ms
2 * * *
3 * * *
Consecutive * * * where the path should continue indicates a DROP/blackhole beyond that hop.
Step 5: If bytes are received, look at the server
curl -v -o /dev/null -w 'ttfb=%{time_starttransfer} total=%{time_total}\n' https://api.example.com/slow
A large ttfb (time to first byte) means the connection is fine but the server is slow — investigate the endpoint, not the network.
Fixes
For a connect-phase timeout, fix the path: open the port in the firewall/security group (change DROP to allow), or add the missing route. Verify with nc:
nc -zv -w5 10.0.6.14 443
Connection to 10.0.6.14 443 port [tcp/https] succeeded!
For a slow endpoint you cannot speed up, raise the timeouts to a sane bound (never unbounded in automation):
curl --connect-timeout 5 --max-time 60 https://api.example.com/health
Add bounded retries with backoff so a transient stall does not fail the whole job:
curl --retry 4 --retry-delay 2 --retry-max-time 120 --connect-timeout 5 --max-time 60 \
https://api.example.com/health
For slow DNS, fix the resolver (or add a short --connect-timeout plus a working nameserver) so resolution does not eat the budget.
What to Watch Out For
- Timeout (28) is silence; refused (7) is an instant RST — a DROP firewall causes 28, a REJECT firewall causes 7. The fixes differ.
with 0 bytes receivedduring connect means packets are dropped; a partial byte count means the server stalled mid-response.- Setting
--max-timetoo low turns healthy-but-slow endpoints into failures; size the timeout to real p99 latency plus headroom. --retryalone does not retry on all timeouts in older curl; pair it with--retry-all-errorswhen appropriate and always bound retries.- Never remove timeouts to “fix” a hang — an unbounded curl in a cron job wedges forever and stacks up runs.
Related Guides
- curl (7) Failed to connect after N ms: Connection refused
- curl (6) Could not resolve host
- Webhook consumer 504 timeout and the retry storm
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.