curl has three ways to stop waiting. --connect-timeout limits the connection, --max-time limits the whole transfer, and --speed-limit with --speed-time stops a transfer that crawls. Without them, curl can wait for a very long time.
We measured seven cases with curl 8.5.0 on Ubuntu 24.04 on 11 October 2026. With no option at all, curl spent 133 seconds on an address that never answered. Each option then stopped curl within a tenth of a second of its limit.
How do you set a timeout in curl?
Set both limits. A short one for connecting, a longer one for the whole request:
# give up if connecting takes over 10 s, or the whole transfer over 30 s
curl --connect-timeout 10 --max-time 30 https://example.com/
The curl manual describes --connect-timeout in one line: "Maximum time in seconds that you allow curl's connection to take." Once connected, curl carries on. --max-time sets "the maximum time in seconds that you allow each transfer to take", from start to finish. The manual says what that is for: "Prevents your batch jobs from hanging for hours due to slow networks or links going down." Both accept decimals, such as --max-time 2.5.
What happens without a timeout?
Less than you might think, and still too long. libcurl's documentation names a "default built-in connection timeout - 300 seconds". In our test that limit never came into play. curl tried to reach 192.0.2.1, an address reserved for documentation by RFC 5737, where nothing ever answers. It gave up after 133 seconds:
$ curl http://192.0.2.1/
curl: (28) Failed to connect to 192.0.2.1 port 80 after 133218 ms: Couldn't connect to server
The operating system stopped it first. On Linux, the kernel resends a connection attempt a limited number of times. Its manual says the default "corresponds to retrying for up to approximately 127 seconds". Our two runs took 133 and 135 seconds.
After the connection, there is no limit at all. For the transfer, libcurl's default is "0 (zero) which means it never times out". A server that accepts the request and then stays silent will hold curl for as long as it likes. We had to cap our own test at 20 seconds.
What did each option do in our test?
We ran each case against 192.0.2.1 or against a slow server of our own on the same machine. One page of that server never answers. The other sends one byte per second.

Every limit held to within a tenth of a second. Every timeout ended with the same exit code, 28, whichever limit was hit.
Which option stops which wait?
A request has three phases, and each option covers a different part of it:
--connect-timeoutcovers the connection only. libcurl spells out what that includes: "the name resolve (DNS) and all protocol handshakes and negotiations until there is an established connection".--max-timecovers everything. It stopped our slow download at 5.0 seconds, with 5 of 30 bytes received.--speed-limitwith--speed-timewatches the speed after the connection. The manual: "If a transfer is slower than this set speed (in bytes per second) for a given number of seconds, it gets aborted." It stopped the slow download at 5.0 seconds, and a silent server too.
A server that answers slowly but steadily never trips the speed limit. Only --max-time stops that one.
What does curl error 28 mean?
The manual's list of exit codes is short on this one: "28 Operation timeout. The specified time-out period was reached according to the conditions." The message after it tells you which limit was hit:
| Message | Limit that was reached |
|---|---|
Failed to connect ... after 5005 ms: Timeout was reached | --connect-timeout |
Failed to connect ... Couldn't connect to server | none set: the system gave up |
Operation timed out after 5006 milliseconds with 5 out of 30 bytes received | --max-time |
Operation too slow. Less than 100 bytes/sec transferred the last 5 seconds | --speed-limit with --speed-time |
The bytes in the --max-time message are useful. 0 bytes means the server never answered. Part of the body means it was too slow.
How do timeouts work through a proxy?
The options work the same, and the proxy becomes part of the connection. We pointed curl at a proxy that never answers:
$ curl --connect-timeout 5 -x http://192.0.2.1:8080 http://example.com/
curl: (28) Failed to connect to 192.0.2.1 port 8080 after 5012 ms: Timeout was reached
The error names the proxy, 192.0.2.1 on port 8080, not example.com. That is the quickest way to tell a dead proxy from a dead site. If the address in the error is your proxy's, change the proxy. Our proxy checker shows which proxies answer before a script waits on them, and our residential proxies are the paid option.
How do retries and timeouts work together?
--retry repeats a transfer after a "transient error", which the manual defines as "a timeout, an FTP 4xx response code or an HTTP 408, 429, 500, 502, 503, 504, 522 or 524 response". We combined it with a 2-second connect limit:
curl --connect-timeout 2 --retry 2 --retry-delay 1 --retry-connrefused http://192.0.2.1/
curl tried three times and stopped after 8.1 seconds: three attempts of 2 seconds, plus the waits. Note one trap. The manual says "the maximum time counter is reset each time the transfer is retried", so --max-time limits each attempt, not the whole run. To cap the whole run, add --retry-max-time.
What this page could not check
- One machine: curl 8.5.0 on Ubuntu 24.04 with the kernel's default settings. Other systems and versions can give other times and messages.
- The slow server was our own program on the same machine. Real servers stall in other ways.
- We did not test slow name lookups or TLS handshakes that stall.
- The run without a timeout took 133 and 135 seconds, a little more than the 127 seconds the Linux manual gives.
- We will run the test again with the current curl by 11 January 2027.
Sources
- curl, man page, --connect-timeout, --max-time, --speed-limit, --speed-time, --retry and exit codes, read 11 October 2026: curl.se.
- libcurl, CURLOPT_CONNECTTIMEOUT, read 11 October 2026: curl.se.
- libcurl, CURLOPT_TIMEOUT, read 11 October 2026: curl.se.
- Linux man-pages, tcp(7), tcp_syn_retries, the version installed on our server, read 11 October 2026: man7.org.
- RFC 5737, IPv4 Address Blocks Reserved for Documentation, IETF, January 2010: rfc-editor.org.
- Our own test: timeouts.sh with curl 8.5.0 on our server, 11 October 2026, 08:46 UTC, and a first run at 08:43 UTC.



