curl transfers data to and from URLs. It’s the Swiss Army knife for anyone running servers: test a website, debug redirects, call an API, check a certificate, measure response times — all from the terminal.
Basics
curl https://example.com # print the response body
curl -o page.html https://example.com # save to a file
curl -O https://example.com/file.tar.gz # save with the remote file name
curl -L https://example.com # follow redirects
curl -s https://example.com # silent (no progress meter)
curl -sS https://example.com # silent, but still show errors
Headers
curl -I https://example.com # HEAD request: headers only
curl -i https://example.com # headers + body
curl -sI https://example.com | grep -i cache-control
curl -sIL http://example.com | grep -iE '^(HTTP|location)' # trace a redirect chain
The last command shows every hop of a redirect — perfect for checking HTTP→HTTPS and www rules. See Nginx redirects.
Send custom headers
curl -H "Accept: application/json" https://api.example.com/items
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me
curl -A "Mozilla/5.0" https://example.com # user agent
curl -H "Host: example.com" http://203.0.113.10/ # test a vhost before DNS points to it
Test a site before DNS changes
--resolve makes curl connect to a specific IP for a hostname — including correct SNI for HTTPS:
curl -I --resolve example.com:443:203.0.113.10 https://example.com/
Indispensable when migrating a site to a new server.
POST data and JSON
curl -X POST -d "name=alice&role=admin" https://example.com/form
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"widget","qty":3}'
curl --json '{"name":"widget"}' https://api.example.com/items # curl 7.82+: sets headers for you
curl -X POST -d @payload.json -H "Content-Type: application/json" https://api.example.com/items
Other methods:
curl -X PUT -d '{"qty":5}' -H "Content-Type: application/json" https://api.example.com/items/1
curl -X PATCH …
curl -X DELETE https://api.example.com/items/1
Upload files
curl -F "[email protected]" https://example.com/upload # multipart form
curl -T backup.zip https://example.com/dav/backup.zip -u user:pass # PUT (WebDAV)
Authentication
curl -u alice https://example.com/admin/ # prompts for password
curl -u alice:secret https://example.com/admin/ # inline (visible in shell history!)
curl --netrc-file ~/.netrc-api https://api.example.com/
Prefer tokens in environment variables over passwords on the command line.
Cookies
curl -c cookies.txt -d "user=alice&pass=…" https://example.com/login
curl -b cookies.txt https://example.com/dashboard
HTTPS and certificates
curl -v https://example.com 2>&1 | grep -E 'SSL|subject|expire|issuer'
curl --cacert ca.pem https://internal.example.com/
curl -k https://self-signed.local/ # skip verification — testing only!
Check expiry quickly:
curl -vsI https://example.com 2>&1 | grep -i 'expire date'
More in check SSL certificate expiry.
Measure response times
curl -o /dev/null -s -w 'dns:%{time_namelookup} connect:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n' https://example.com
- dns — name resolution
- connect — TCP connected
- tls — TLS handshake done
- ttfb — first byte from the server (server processing time shows here)
- total — complete
A high TTFB with fast connect points to slow application code or database; a slow connect points to network distance — see latency vs bandwidth.
Just the status code:
curl -o /dev/null -s -w '%{http_code}\n' https://example.com
Downloads
curl -C - -O https://example.com/big.iso # resume an interrupted download
curl --limit-rate 2M -O https://example.com/big.iso
curl -O https://example.com/file[1-5].txt # URL globbing: file1…file5
Debugging
curl -v https://example.com # request/response headers and TLS details
curl --trace-ascii trace.txt https://example.com
curl --http1.1 https://example.com # force HTTP version
curl --http2 -I https://example.com
curl -4 https://example.com # IPv4 only (-6 for IPv6)
curl --connect-timeout 5 --max-time 20 https://example.com
Health checks in scripts
if curl -fsS --max-time 10 https://example.com/health > /dev/null; then
echo up
else
echo down
fi
-f makes curl return a non-zero exit code for HTTP errors (4xx/5xx) — essential in scripts and cron jobs.
Quick reference
| Task | Command |
|---|---|
| Headers only | curl -I URL |
| Follow redirects | curl -L URL |
| Save file | curl -O URL |
| JSON POST | curl --json '{…}' URL |
| Custom header | curl -H "K: V" URL |
| Basic auth | curl -u user URL |
| Status code | curl -o /dev/null -s -w '%{http_code}' URL |
| Timing | curl -w '…%{time_total}' -o /dev/null -s URL |
| Fixed IP | curl --resolve host:443:IP URL |
| Fail on errors | curl -f |
Also see wget examples for recursive downloads and mirroring.