cURL Timeout: Settings, Errors, and Best Practices

curl timeout

In the world of web development and automation, there is nothing more frustrating than a script that simply “hangs.” You initiate a request, and then… nothing. Minutes pass, your system resources are tied up, and the task remains unfinished. This is almost always the result of a missing or misconfigured timeout.

While cURL is an incredibly powerful tool for transferring data across nearly every protocol, its default behavior can lead to these indefinite waits. This guide dives deep into cURL timeout settings, showing you exactly how to control your connections and keep your scripts running reliably.

What Is a cURL Timeout?

To answer the most common question first: Does cURL have a timeout by default? Yes — but not in the way most developers expect. By default, cURL applies a connection timeout of around 300 seconds (5 minutes). However, once the connection is established, cURL may wait indefinitely for a response unless you explicitly set an overall time limit. This is the most common reason scripts appear to freeze.

A cURL timeout is a configuration parameter that tells the tool how long to wait at a specific stage of communication before giving up and closing the connection. Properly configured timeouts are essential for building reliable applications. For example, if you are building a real-time price tracker, you cannot afford to have one slow server block your entire request queue. A well-configured timeout lets cURL fail fast, log the error, and move on.

Timeout management is critical for several reasons:

  • Resource Management: Prevents “zombie” processes from consuming CPU and memory indefinitely.
  • Benutzererfahrung: Ensures your application returns an error quickly instead of an endless loading spinner.
  • Skalierbarkeit: Essential for web scrapers and crawlers processing thousands of requests per hour.

Understanding the “what” is the first step. Now let’s look at the most common causes of timeouts in practice.

Why Is Your cURL Request Timing Out?

A timeout error is rarely a random occurrence. It is usually a symptom of a deeper issue in the network chain. Identifying the root cause is the key to choosing the right fix.

1. DNS Resolution Issues

Before cURL can communicate with a server, it must resolve the server’s hostname to an IP address. If the DNS server is slow or unresponsive, cURL will stall right at the start of the request. You can use the --dns-servers flag to point cURL at a faster provider — for example, Google’s public DNS at 8.8.8.8 — to speed up this initial phase.

2. Server-Side Throttling and Web Application Firewalls (WAFs)

When you make too many requests in a short period, the target server’s WAF may silently drop your packets rather than returning an error code. cURL will then wait until it hits your configured timeout limit, making it appear as though the server is simply not responding.

3. Poor-Quality Proxy Servers

This is one of the most frequent causes of cURL timeout errors for developers. An overloaded or geographically distant free proxy can introduce substantial latency. Switching to a premium proxy service with dedicated, high-speed residential or ISP IPs can dramatically reduce connection timeout failures.

Understanding How cURL Timeout Options Relate to Each Other

Before configuring timeout values, it is important to understand how the different options interact. This relationship catches many developers off-guard:

Key rule: --connect-timeout is counted within --Max-Zeit. If you set --connect-timeout 4 und --max-time 2, the operation can never last longer than 2 seconds — the shorter limit always wins. Always make --Max-Zeit larger than --connect-timeout.

Here is a visual breakdown of the three phases cURL passes through during a request, and which option governs each:

┌─────────────────────────────────────────────────────────────────┐
│                       --max-time (total cap)                    │
│                                                                 │
│  ┌──────────────────────────┐  ┌──────────────────────────────┐ │
│  │  --connect-timeout       │  │  Data Transfer Phase         │ │
│  │  (DNS + TCP + TLS)       │  │  (governed only by max-time) │ │
│  └──────────────────────────┘  └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Common trap: Verwendung von --retry without --connect-timeout may not work as expected. cURL’s retry logic only triggers when a timed-out connection attempt is detected. Without an explicit --connect-timeout, some connection failures are classified as hard errors and are not retried. Always pair --retry mit --connect-timeout.

Common cURL Timeout Error Codes

When cURL fails due to time constraints, it returns a specific exit code. Knowing what these codes mean will save you hours of debugging.

Exit Code Error Message Most Likely Cause
28 Operation timed out The request exceeded the time limit set by --Max-Zeit oder --connect-timeout. The most common timeout error.
7 Failed to connect to host cURL could not reach the server at all — wrong port, server is down, or a local firewall is blocking outbound connections.
52 Empty reply from server The connection was established, but the server closed it without sending any data. Often caused by an overloaded server.
35 SSL/TLS handshake timeout The TLS negotiation took too long. Common on servers with misconfigured certificates or under heavy network congestion.

You can check the exit code in your shell immediately after a cURL command:

curl --connect-timeout 5 --max-time 15 https://example.com
echo $?   # prints 28 if a timeout occurred

cURL Timeout Options: Reference Table

cURL provides several flags that give you granular control over how long a request is allowed to take. The following table summarizes the most important ones.

Option What It Controls Typical Value Notes
--Max-Zeit / -m Total wall-clock time for the entire operation (connection + transfer) 15–60s for APIs; 300s+ for large files Hard cap; cURL aborts regardless of transfer progress
--connect-timeout Time allowed for the connection phase only (DNS + TCP + TLS handshake) 3–10s Must be smaller than --Max-Zeit
--speed-limit Minimum transfer speed in bytes per second 1024 (1 KB/s) to 1048576 (1 MB/s) Use with --speed-time to abort stalled downloads
--speed-time Seconds the speed must stay below --speed-limit before aborting 15–30s Prevents killing briefly slow but healthy transfers
--retry Number of automatic retry attempts on transient failures 3–5 Retries on exit code 28, HTTP 408/429/5xx. Requires --connect-timeout to retry connection-phase failures.
--retry-max-time Maximum total time allowed across all retry attempts 60–120s Prevents retries from running indefinitely

Practical Examples by Use Case

1. API Request — Fast Fail

For API calls, you want a quick response or a quick failure. A short connection timeout catches dead servers immediately, while a moderate total timeout allows for legitimate processing time.

curl \
  --connect-timeout 5 \    # fail fast if server is unreachable
  --max-time 20 \           # total cap for the whole request
  --fail \                  # exit code 22 on HTTP 4xx/5xx (useful in scripts)
  --silent \
  --show-error \
  https://api.example.com/data

2. Large File Download — Speed Guard

For large files, a hard --Max-Zeit is dangerous because it may cut off a legitimate slow download. Use --speed-limit und --speed-time instead to ensure the transfer is at least progressing.

curl \
  --connect-timeout 10 \        # allow extra time for initial connection
  --speed-limit 1048576 \       # abort if speed drops below 1 MB/s...
  --speed-time 30 \             # ...and stays below it for 30 seconds
  --output largefile.zip \
  https://example.com/largefile.zip

3. Retry Logic — Resilient Script

For automation where transient failures are expected, combine timeout options with retry flags. cURL’s default retry behavior applies automatic exponential backoff (starting at 1 second, doubling up to 10 minutes).

curl \
  --connect-timeout 5 \         # required for retry to catch connection timeouts
  --max-time 30 \               # per-attempt time limit
  --retry 3 \                   # retry up to 3 times on transient errors
  --retry-delay 2 \             # wait 2 seconds between retries
  --retry-max-time 90 \         # cap total retry window at 90 seconds
  --retry-connrefused \         # also retry on "connection refused"
  --fail \
  --silent --show-error \
  https://api.example.com/endpoint
Important: Only use --retry-all-errors with idempotent requests (GET, HEAD). Never use it with POST or PATCH — retrying those requests may cause duplicate records or side effects on the server.

4. Proxy Integration

When routing through a proxy, you are adding an extra network hop. Allow slightly more time for the connection phase to account for this routing overhead.

curl \
  --proxy http://proxy.example.com:8080 \
  --connect-timeout 10 \        # extra time for proxy routing
  --max-time 30 \
  --retry 3 \
  --retry-delay 2 \
  https://target-site.com/data

5. Debugging — Measure Each Phase

If you cannot determine where a timeout is occurring, use --write-out to measure each stage independently. This pinpoints whether the delay is in DNS, the TCP handshake, TLS negotiation, or the server response.

curl \
  --connect-timeout 10 \
  --max-time 30 \
  --silent --output /dev/null \
  --write-out "DNS:     %{time_namelookup}s\nConnect: %{time_connect}s\nTLS:     %{time_appconnect}s\nTTFB:    %{time_starttransfer}s\nTotal:   %{time_total}s\nHTTP:    %{http_code}\n" \
  https://example.com

Sample output:

DNS:     0.012s
Connect: 0.058s
TLS:     0.187s
TTFB:    0.245s
Total:   0.247s
HTTP:    200

Wenn DNS is abnormally high, the bottleneck is in name resolution. If TLS is high, it is in the SSL handshake. This narrows the problem immediately.

Best Practices for cURL Timeout Management

  • Always use the dual-timeout strategy. Set both --connect-timeout und --Max-Zeit together. One covers a dead server; the other caps total transfer time. Neither alone is sufficient.
    curl --connect-timeout 5 --max-time 20 https://api.example.com
  • Always pair --retry mit --connect-timeout. Without an explicit connection timeout, cURL may not classify connection failures as retriable transient errors, and retries will silently not occur.
  • Make --Max-Zeit larger than --connect-timeout. Since --connect-timeout counts against --Max-Zeit, setting them equal or inverting them means the total cap may fire before the connection phase even finishes.
  • Use decimal values for sub-second precision. Since cURL 7.32.0, you can write --connect-timeout 0.5 to set a 500 ms limit. This is useful for latency-sensitive microservices or health checks.
  • Verwenden Sie --speed-limit / --speed-time for large downloads instead of --Max-Zeit. A hard total-time cap will interrupt a legitimate large download. Speed-based limits catch only truly stalled transfers.
  • Add --fail in scripts. By default, cURL exits with code 0 even when the server returns an HTTP 4xx or 5xx status. The --fail flag causes cURL to exit with code 22 on those responses, making error detection reliable in shell scripts.
  • Verwenden Sie --head (-I) for server health checks. Fetching only the HTTP headers is faster than downloading a full response body.
    curl -I --connect-timeout 3 --max-time 5 https://example.com
  • Set language-level timeouts slightly higher than cURL timeouts. When running cURL inside a PHP, Python, or Node.js process, your script-level timeout should be slightly longer than your cURL timeout. This lets cURL handle the failure cleanly rather than being killed mid-operation by the parent process.

Advanced Techniques

Verbose Logging for Debugging

When you cannot determine why a request is timing out, use -v (verbose) or --trace-ascii. These show you exactly where the clock stops — during DNS lookup, the TLS handshake, or while waiting for the first byte from the server.

# Print verbose output to a log file for later analysis
curl \
  --connect-timeout 10 \
  --max-time 30 \
  --verbose \
  --stderr curl_debug.log \
  https://example.com

Comprehensive Retry with Logging

For production scripts, combine timeout controls with retry logic and output logging to build a fully observable request pipeline:

curl \
  --connect-timeout 5 \
  --max-time 30 \
  --retry 3 \
  --retry-delay 2 \
  --retry-max-time 90 \
  --retry-all-errors \          # only safe for GET/HEAD requests
  --fail \
  --silent --show-error \
  --write-out "\nStatus: %{http_code} | Total: %{time_total}s\n" \
  https://api.example.com/data

Timeout Settings in Programming Languages

If you are using cURL through a library rather than the command line, the same principles apply — just through the language’s API:

PHP (libcurl):

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);   // connection phase
curl_setopt($ch, CURLOPT_TIMEOUT, 20);          // total operation

Python (requests library):

# Pass a tuple: (connect_timeout, read_timeout)
response = requests.get(url, timeout=(5, 20))

Node.js (node-fetch / axios):

// axios: set a total timeout in milliseconds
axios.get(url, { timeout: 20000 });

Schlussfolgerung

Mastering cURL timeout configuration is one of the most effective ways to improve the stability and reliability of your network-dependent applications. By replacing cURL’s default passive behavior with explicit --connect-timeout und --Max-Zeit parameters — and pairing them correctly with retry logic — you protect your system resources and eliminate the “infinite wait” problem entirely.

The key takeaways:

  • Always set both --connect-timeout und --Max-Zeit (the dual-timeout strategy).
  • --connect-timeout counts within --Max-Zeit — set the latter higher.
  • Always include --connect-timeout when using --retry to ensure connection-phase failures are actually retried.
  • For large file downloads, prefer speed-based limits over a hard total-time cap.
  • Verwenden Sie --write-out with timing variables to diagnose exactly where delays occur.

A failed request that terminates in 5 seconds is almost always preferable to one that hangs for 5 minutes. With the techniques in this guide, you now have the tools to ensure every request in your stack behaves predictably.