Skip to content
Go back

curl Flags Every Developer Should Know

By SumGuy 5 min read
curl Flags Every Developer Should Know

The Flag Forest

curl has roughly 237 different flags. You’ll use about 6 of them 95% of the time. But knowing the next handful? That’s where you stop cargo-culting Stack Overflow answers and start solving actual problems.

Let’s talk about the flags that matter—the ones that’ll keep you from SSH-ing into a server at 2 AM to debug a flaky webhook.

The Core Six You Already Know

These are your bread and butter. If you don’t know these, go practice for five minutes:

Terminal window
curl -X POST https://api.example.com/endpoint # HTTP method
curl -H "Authorization: Bearer token123" https://... # Custom header
curl -d '{"key":"value"}' https://... # POST data
curl -u username:password https://... # Basic auth
curl -L https://... # Follow redirects
curl -o filename.json https://... # Save to file

Standard stuff. You’ve used all of these. But here’s where it gets interesting.

The Ones That Save You Hours

-w — Output Formatting (The Detective Flag)

Want to know what actually happened during that request? Not just the body, but headers, timing, response code, all of it?

Terminal window
curl -w "Status: %{http_code}\nTime: %{time_total}s\nRedirects: %{num_redirects}\n" \
https://api.example.com/endpoint

That %{...} syntax is your friend. Some useful ones:

Terminal window
curl -w "
DNS lookup: %{time_namelookup}s
Connection: %{time_connect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
Size: %{size_download} bytes
Status: %{http_code}
" https://api.example.com

This tells you exactly where requests are getting slow. Is it DNS? The connection? The server thinking? Now you know.

-v and -vv — Verbose Mode

Terminal window
curl -v https://api.example.com/endpoint

Shows you request headers, response headers, SSL/TLS details. The arrow notation:

> GET /endpoint HTTP/1.1
> Host: api.example.com
> User-Agent: curl/8.1.2
< HTTP/1.1 200 OK
< Content-Type: application/json

Use -vv for even more detail (including HTTP/2 frame information if you’re deep troubleshooting).

Save cookies from one request, reuse them in another:

Terminal window
curl -c cookies.txt https://api.example.com/login \
-d '{"user":"you","pass":"secret"}'
# Use those cookies on the next request:
curl -b cookies.txt https://api.example.com/protected

Lifesaver when you’re testing authenticated flows.

--data-raw — Raw POST Data (No Escaping)

Tired of fighting with quote escaping in shell scripts?

Terminal window
curl --data-raw '{"message":"Don'\''t escape me"}' https://api.example.com

--data-raw treats the string literally. No shell interpolation weirdness.

-H @headers.txt — Load Headers from File

When you’re building complex requests, write headers to a file:

Terminal window
# headers.txt:
Authorization: Bearer eyJhbGc...
X-Custom-Header: value123
Content-Type: application/json

Then:

Terminal window
curl -H @headers.txt -d '{"key":"value"}' https://api.example.com

Cleaner than stacking -H flags ten deep.

--compressed — Automatic Decompression

Most APIs return gzip-compressed responses. curl can auto-decompress:

Terminal window
curl --compressed https://api.example.com/endpoint

Saves bandwidth on large payloads.

--max-time — Request Timeout

Prevent hanging indefinitely on dead servers:

Terminal window
curl --max-time 10 https://api.example.com # 10 second timeout

Pair it with -m (same thing, shorter flag). Essential for monitoring scripts.

The One That Stops Redirects Cold

Terminal window
curl -L --max-redirs 3 https://example.com # Follow up to 3 redirects

Some APIs redirect you to hell and back. Cap it. Prevents infinite loops and makes debugging obvious.

Real-World Example: Debugging an API

Terminal window
# Full diagnostics:
curl -v -w "\n\nStatus: %{http_code}\nTotal time: %{time_total}s\n" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--compressed \
--max-time 5 \
-d '{"action":"test"}' \
https://api.example.com/webhook

That one command tells you:

Worth memorizing? Absolutely.

Advanced: Conditional Requests

Sometimes you only want to download if the file changed:

Terminal window
# Download only if server has a newer version:
curl -z last-modified.txt -o file.txt https://example.com/file.txt
# The -z flag checks the local file's modification time
# If remote is older, nothing downloads (saves bandwidth)

Or use ETags for precise cache validation:

Terminal window
# Save the ETag from first request:
curl -i https://api.example.com/data | grep -i etag
# Later, check if it's the same:
curl -H 'If-None-Match: "abc123"' https://api.example.com/data
# If unchanged, server returns 304 Not Modified (no body download)

This is crucial for monitoring scripts that poll APIs—you stop downloading the same gigabyte of data over and over.

Building Scripts That Don’t Break

When curl appears in a bash script, make it defensive:

Terminal window
# Fail immediately if curl fails:
set -e
# Set timeout to prevent hanging:
curl --max-time 10 \
--retry 3 \
--retry-delay 5 \
https://api.example.com/endpoint || exit 1
echo "Request succeeded"

The --retry flags are criminally underused. Three retries with five-second delays handles temporary network blips without making your script fragile.

For loops over many URLs:

Terminal window
urls=("https://api.example.com/1" "https://api.example.com/2")
for url in "${urls[@]}"; do
curl --silent --show-error \
--max-time 10 \
--retry 2 \
"$url" || echo "Failed: $url"
done

--silent kills progress meters (noisy in scripts), --show-error still shows real errors. Clean output.

The Trick Nobody Remembers

Want to see what curl is actually sending?

Terminal window
curl --trace - https://api.example.com/endpoint 2>&1 | head -20

That --trace - writes trace output to stdout (normally goes to stderr). You see everything: SSL negotiation, every byte sent and received, all headers.

=> Send header
=> GET / HTTP/1.1
=> Host: api.example.com
=> User-Agent: curl/7.68.0
...
<= Recv header
<= HTTP/1.1 200 OK
<= Content-Type: application/json

When something weird is happening and -v isn’t verbose enough, --trace shows the truth.

The trick isn’t knowing all 237 flags. It’s knowing these dozen ones so well you don’t need to think about them. That’s when you’re actually productive instead of fighting the tool.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
BookStack vs Wiki.js: Picking Your Self-Hosted Documentation Platform
Next Post
Paperless-ngx: Scan It, Forget It, Find It Instantly

Related Posts