▸ Basics
Simplest request
curl https://example.com
Save the response body
curl -o page.html https://example.com curl -O https://example.com/file.zip # use remote filename
Silent / quiet
curl -sS https://api.example.com # silent, but show errors
Follow redirects
curl -L https://example.com
Show only response headers
curl -I https://example.com # HEAD request curl -i https://example.com # GET, include headers in output curl -D - https://example.com # dump headers to stdout
▸ HTTP methods
curl -X GET https://api.example.com/users curl -X POST https://api.example.com/users curl -X PUT https://api.example.com/users/1 curl -X PATCH https://api.example.com/users/1 curl -X DELETE https://api.example.com/users/1 curl -X OPTIONS https://api.example.com
Tip:
-X is rarely needed — curl infers the method from -d (POST), -T (PUT), -I (HEAD). Don't combine -X POST with -L blindly — redirects may downgrade.HTTP version
curl --http1.1 https://example.com curl --http2 https://example.com curl --http2-prior-knowledge https://example.com curl --http3 https://example.com # requires HTTP/3 build
▸ Headers
Send a header
curl -H "Accept: application/json" https://api.example.com curl -H "X-Request-Id: abc123" \ -H "User-Agent: my-client/1.0" \ https://api.example.com
Remove a default header
curl -H "Accept:" https://api.example.com # empty value removes it
Replace a header with empty value
curl -H "X-Trace;" https://api.example.com # trailing ; → empty value
User-Agent / Referer shortcuts
curl -A "Mozilla/5.0" https://example.com curl -e "https://google.com" https://example.com
▸ Request body
URL-encoded form (application/x-www-form-urlencoded)
curl -d "user=alice&pass=secret" https://example.com/login curl --data-urlencode "q=hello world" https://example.com
JSON body
curl -H "Content-Type: application/json" \ -d '{"name":"alice","age":30}' \ https://api.example.com/users # curl 7.82+ shorthand: curl --json '{"name":"alice"}' https://api.example.com/users
Tip:
--json sets Content-Type and Accept to application/json automatically.Body from a file
curl -d "@payload.json" \ -H "Content-Type: application/json" \ https://api.example.com curl --data-binary "@file.bin" https://example.com # preserve newlines
Multipart form (multipart/form-data)
curl -F "name=alice" \ -F "avatar=@./me.png" \ -F "[email protected];type=application/json" \ https://example.com/upload
Query string params (URL-encoded)
curl -G \ --data-urlencode "q=hello world" \ --data-urlencode "page=1" \ https://example.com/search # → GET /search?q=hello%20world&page=1
▸ Authentication
Basic
curl -u "alice:secret" https://api.example.com curl -u "alice" https://api.example.com # prompts for password
Bearer token
curl -H "Authorization: Bearer $TOKEN" https://api.example.com curl --oauth2-bearer "$TOKEN" https://api.example.com
Digest, NTLM, Negotiate
curl --digest -u "user:pass" https://example.com curl --ntlm -u "user:pass" https://example.com curl --negotiate -u ":" https://example.com # Kerberos / SPNEGO
AWS SigV4
curl --aws-sigv4 "aws:amz:us-east-1:s3" \ -u "$AWS_ACCESS_KEY:$AWS_SECRET_KEY" \ https://my-bucket.s3.us-east-1.amazonaws.com/key
Caution: Passwords on the CLI leak into shell history and
ps. Prefer -u user with a password prompt, a .netrc file, or --config..netrc
curl --netrc https://api.example.com curl --netrc-file ~/.netrc-prod https://api.example.com
▸ Download & upload
Resume an interrupted download
curl -C - -O https://example.com/big.iso
Limit transfer rate
curl --limit-rate 200K -O https://example.com/big.iso
Range / partial fetch
curl -r 0-1023 -o first-1kb.bin https://example.com/file
Upload with PUT
curl -T ./local.txt https://example.com/remote.txt curl -T ./local.txt sftp://user@host/path/
Make missing parent directories (FTP/SFTP)
curl --ftp-create-dirs -T file ftp://host/new/dir/
▸ Cookies & sessions
Send cookies
curl -b "sid=abc123" https://example.com curl -b cookies.txt https://example.com
Save received cookies (cookie jar)
curl -c cookies.txt https://example.com/login
Round-trip a session
curl -c jar.txt -d "user=a&pass=b" https://example.com/login curl -b jar.txt -c jar.txt https://example.com/dashboard
▸ TLS / certificates
Skip verification (dev only!)
curl -k https://self-signed.local curl --insecure https://self-signed.local
Custom CA bundle
curl --cacert corp-ca.pem https://internal.corp curl --capath /etc/ssl/certs https://example.com
Client certificates (mTLS)
curl --cert client.crt --key client.key https://mtls.example.com curl --cert client.p12:password --cert-type P12 https://example.com
Pinning
curl --pinnedpubkey "sha256//AAAA....=" https://example.com
TLS version
curl --tlsv1.2 --tls-max 1.3 https://example.com
Show negotiated cipher / cert details
curl -vI https://example.com 2>&1 | grep -E "SSL|TLS|subject|issuer"
▸ Proxy & network
HTTP/HTTPS proxy
curl -x http://proxy:8080 https://example.com curl --proxy-user "u:p" -x http://proxy:8080 https://example.com
SOCKS5
curl --socks5 localhost:1080 https://example.com curl --socks5-hostname localhost:1080 https://example.com # DNS via proxy
Force IPv4 / IPv6
curl -4 https://example.com curl -6 https://example.com
Resolve hostname to fixed IP
curl --resolve example.com:443:1.2.3.4 https://example.com curl --connect-to example.com:443:newhost:443 https://example.com
Bind to interface / source IP
curl --interface eth0 https://example.com curl --interface 10.0.0.5 https://example.com
Connect to Unix socket
curl --unix-socket /var/run/docker.sock http://localhost/containers/json
▸ Redirects & retries
curl -L https://example.com curl -L --max-redirs 5 https://example.com curl -L --location-trusted https://example.com # resend auth on redirect
Retries (network errors / 5xx)
curl --retry 5 \ --retry-delay 2 \ --retry-max-time 60 \ --retry-all-errors \ https://api.example.com
Timeouts
curl --connect-timeout 5 \ --max-time 30 \ https://example.com
Fail-fast on HTTP errors
curl -f https://example.com # exit 22 on 4xx/5xx, hide body curl --fail-with-body https://example.com # same, but keep body
▸ Output & timing
Get just the HTTP status code
curl -o /dev/null -s -w "%{http_code}\n" https://example.com
Full timing breakdown
curl -o /dev/null -s -w "\ dns: %{time_namelookup}s\n\ connect: %{time_connect}s\n\ tls: %{time_appconnect}s\n\ ttfb: %{time_starttransfer}s\n\ total: %{time_total}s\n\ size: %{size_download} bytes\n\ status: %{http_code}\n" https://example.com
Useful -w variables
| Variable | Meaning |
|---|---|
%{http_code} | response status |
%{url_effective} | final URL after redirects |
%{redirect_url} | next URL when redirecting |
%{remote_ip} | resolved peer IP |
%{ssl_verify_result} | 0 = ok |
%{size_download} | bytes received |
%{speed_download} | bytes/sec |
%{json} | all timing fields as JSON (7.70+) |
▸ Debugging
curl -v https://example.com # verbose curl -vv https://example.com # even more curl --trace trace.log https://example.com curl --trace-ascii trace.log https://example.com curl --trace-time -v https://example.com # prepend timestamps
Show DNS resolution path
curl --trace-config all -v https://example.com
Show curl version & protocols
curl --version curl-config --features
Pro tip: Browser DevTools → right-click any request → Copy as cURL. Replays the exact request from your terminal — invaluable for debugging API issues.
▸ Parallel & multi-URL
Multiple URLs in one invocation
curl https://example.com/1 https://example.com/2
Parallel (curl 7.66+)
curl -Z --parallel-max 10 \ https://example.com/[1-100].json \ -o "out/#1.json"
URL globbing
curl https://example.com/file[1-5].txt -O curl https://example.com/{red,green,blue}.png -O curl https://example.com/page[1-100:2].html -O # step 2
Disable globbing (URL has literal brackets)
curl -g "https://example.com/[literal]"
▸ Config files & env
~/.curlrc (auto-loaded)
# Lines are long-option-name = value user-agent = "my-cli/1.0" connect-timeout = 10 retry = 3 silent
Per-call config
curl -K request.curl
request.curl:
url = "https://api.example.com/users" request = "POST" header = "Content-Type: application/json" data = '{"name":"alice"}'
Useful env vars
| Var | Effect |
|---|---|
HTTP_PROXY / HTTPS_PROXY | per-scheme proxy |
NO_PROXY | comma-sep hosts to bypass |
CURL_HOME | override ~/.curlrc dir |
CURL_SSL_BACKEND | e.g. openssl, secure-transport |
SSLKEYLOGFILE | dump TLS keys for Wireshark |
▸ Other protocols
FTP / FTPS
curl -u "user:pass" ftp://ftp.example.com/dir/ curl -u "user:pass" -T file.zip ftp://ftp.example.com/uploads/ curl --ssl-reqd ftp://ftp.example.com # require TLS
SFTP / SCP
curl -u "user:" --key ~/.ssh/id_ed25519 sftp://host/path/ curl -u "user:" scp://host:22/etc/hostname
SMTP (send email)
curl --ssl-reqd \ --url smtps://smtp.example.com:465 \ --user "[email protected]:pw" \ --mail-from [email protected] \ --mail-rcpt [email protected] \ --upload-file message.eml
IMAP
curl -u "alice:pw" imaps://imap.example.com/INBOX # list curl -u "alice:pw" "imaps://imap.example.com/INBOX;UID=42" # fetch
WebSocket (curl 7.86+)
curl --include \ -H "Sec-WebSocket-Version: 13" \ wss://echo.example.com
MQTT, DICT, LDAP, TFTP, GOPHER
curl dict://dict.org/d:curl curl mqtt://broker.example.com/topic curl ldap://ldap.example.com/dc=example,dc=com
▸ Real-world recipes
JSON POST + jq pipeline
curl -sS --json '{"q":"curl"}' https://api.example.com/search | jq '.hits[].title'
Token-aware API call
TOKEN="$(cat ~/.config/myapp/token)" curl -sS --fail-with-body \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/json" \ https://api.example.com/me
Test endpoint health in a script
code=$(curl -o /dev/null -s -w "%{http_code}" --max-time 5 https://api.example.com/health) if [ "$code" != "200" ]; then echo "unhealthy: $code" >&2 exit 1 fi
Mirror a static site
curl -O https://example.com/index.html # for real mirroring use wget; curl has no recursive mode
Test redirect chain
curl -sIL -o /dev/null -w "%{http_code} → %{url_effective}\n" \ https://bit.ly/abc
Reproduce a flaky request
for i in {1..50}; do curl -o /dev/null -s -w "%{http_code} %{time_total}s\n" \ https://api.example.com/v1/widgets done | sort | uniq -c
Capture TLS keys for Wireshark
SSLKEYLOGFILE=/tmp/keys.log curl https://example.com
OAuth2 client_credentials grant
curl -sS -X POST https://auth.example.com/oauth/token \ -u "$CLIENT_ID:$CLIENT_SECRET" \ -d "grant_type=client_credentials" \ -d "scope=read:widgets" | jq -r .access_token
GitHub API: list your repos
curl -sS \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer $GH_TOKEN" \ https://api.github.com/user/repos?per_page=100