TechEarl

curl Cheat Sheet: HTTP Requests, Headers, Auth, and Common Flags

A scannable curl reference: GET, POST, PUT, DELETE; JSON and form bodies; basic, bearer, and digest auth; redirects, retries, timeouts; --resolve overrides, SOCKS proxies; with PowerShell Invoke-WebRequest equivalents.

Ishan KarunaratneIshan Karunaratne⏱️ 13 min readUpdated
curl cheat sheet: GET, POST, PUT, DELETE; -d JSON body, -H headers, -u basic auth, -L redirects, -f fail on 4xx/5xx, -sS silent with errors; --resolve DNS override, --socks5 proxy, --max-time; Invoke-RestMethod on Windows.

curl is the universal HTTP client: every API debug session, every webhook test, every "is the server up?" check eventually runs through it. The flag surface is huge but you only use a dozen of them daily. This page is the reference for those flags, the JSON / form / multipart body forms, the auth modes, and the safety nets (-f, --max-time, --retry) that turn one-off commands into reliable script primitives.

How do I use curl?

curl sends an HTTP, HTTPS, FTP, SFTP, or one of many other protocol requests and prints the response to stdout. For HTTP, the basic invocation is curl URL (a GET request). Change the method with -X POST / -X PUT / -X DELETE. Send a JSON body with -d '{}' -H 'Content-Type: application/json'. Send form data with -d 'key=value'. Add headers with -H 'Name: Value'. Authenticate with -u user:pass (basic), -H 'Authorization: Bearer TOKEN' (bearer), or --digest (digest). Save the response with -o file (specified name) or -O (use URL's filename). Critical safety flags for scripts: -f (fail on HTTP 4xx/5xx), -sS (silent but show errors), -L (follow redirects), --max-time and --connect-timeout (bound the request). Windows 10 1803+ ships real curl in C:\Windows\System32\curl.exe (the PowerShell curl alias to Invoke-WebRequest only intercepts in the absence of explicit curl.exe).

Try it with your own values

Jump to:

Basic requests

GET (the default):

bash· Linux (GNU)
curl https://api.example.com/users

Note on Windows: PowerShell aliases curl to Invoke-WebRequest for backwards compat. Always call curl.exe explicitly in PowerShell to get the real binary. Inside cmd.exe and on Windows 10 1803+, curl resolves directly to curl.exe.

POST with a JSON body:

bash· Linux (GNU)
curl -X POST -H 'Content-Type: application/json' -d '{"name":"Ishan"}' https://api.example.com/users

PowerShell's parser needs the inner double quotes escaped. The alternative, much cleaner, is Invoke-RestMethod:

bash· Linux (GNU)
# Linux equivalent already shown above with curl

PUT and DELETE follow the same pattern with -X PUT and -X DELETE. HEAD has a dedicated flag, -I:

bash· Linux (GNU)
curl -I https://example.com

Useful for checking redirects, content types, cache headers, and HSTS without downloading the body.

Sending data: JSON, form, multipart

-d sends the body. By default it sets Content-Type: application/x-www-form-urlencoded and uses POST. For JSON, set the header explicitly:

bash· Linux (GNU)
curl -X POST -H 'Content-Type: application/json' -d '{"event":"login"}' https://api.example.com/events

Read body from a file (handy for large JSON payloads):

bash· Linux (GNU)
curl -X POST -H 'Content-Type: application/json' -d @body.json https://api.example.com/events

URL-encoded form (Content-Type: application/x-www-form-urlencoded):

bash· Linux (GNU)
curl -d 'name=Ishan&role=admin' https://api.example.com/users

--data-urlencode handles special characters safely (use one per field):

bash· Linux (GNU)
curl --data-urlencode 'q=hello world' --data-urlencode 'lang=en' https://api.example.com/search

Multipart file upload (Content-Type: multipart/form-data):

bash· Linux (GNU)
curl -F 'file=@./avatar.png' -F 'name=avatar' https://api.example.com/upload

-F always uses POST and multipart, regardless of -X. The @ prefix tells curl to read the field value from a file. For arbitrary binary uploads (PUT, no multipart wrapper), use --data-binary @file with the right Content-Type.

Headers

-H 'Name: Value' adds or overrides a single header. Repeat for each header.

bash· Linux (GNU)
curl -H 'Accept: application/json' -H 'X-Request-ID: 1234' https://api.example.com/users

Override the User-Agent (the default is curl/X.Y.Z):

bash· Linux (GNU)
curl -A 'Mozilla/5.0' https://example.com

Show response headers along with the body (verbose mode shows request headers too):

bash· Linux (GNU)
curl -i https://api.example.com/users

Full request and response trace:

bash· Linux (GNU)
curl -v https://api.example.com/users

-v is the first thing I add when an API call fails for unclear reasons. It prints every header sent and received plus TLS negotiation details.

Authentication

Basic auth (sends Authorization: Basic base64(user:pass)):

bash· Linux (GNU)
curl -u username:password https://api.example.com/protected

Prompt for the password (does not appear in shell history):

bash· Linux (GNU)
curl -u username https://api.example.com/protected

Bearer token (the OAuth / JWT pattern):

bash· Linux (GNU)
curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIs...' https://api.example.com/me

Digest auth (legacy, rare):

bash· Linux (GNU)
curl --digest -u user:pass https://api.example.com/protected

For client certificates (mTLS):

bash· Linux (GNU)
curl --cert client.pem --key client.key https://api.example.com/mtls

Following redirects and saving output

-L follows redirects (otherwise curl prints the 3xx response and stops):

bash· Linux (GNU)
curl -L https://example.com

-o filename saves the response body to a specified path:

bash· Linux (GNU)
curl -o output.html https://example.com

-O (capital O) saves using the URL's basename as the filename:

bash· Linux (GNU)
curl -O https://example.com/files/report.pdf

Combine with -L for downloads that go through a redirect (CDN, github releases):

bash· Linux (GNU)
curl -LO https://github.com/owner/repo/releases/latest/download/asset.tar.gz

Resume a partial download with -C -:

bash· Linux (GNU)
curl -C - -O https://example.com/large.iso

Failure and silence: -f, -sS

-f makes curl return a non-zero exit code on HTTP 4xx/5xx. Without it, curl exits 0 for any HTTP response, which silently breaks scripts that rely on exit status.

bash· Linux (GNU)
curl -f https://api.example.com/users || echo 'request failed'

-s is silent (no progress bar, no errors). -S re-enables error messages. The combination -sS is the standard "quiet but visible failures" mode for scripts:

bash· Linux (GNU)
curl -fsSL https://example.com/install.sh | bash

-fsSL is the four-flag combo I use for "download this script and run it" patterns: fail on HTTP error, silent progress, show errors, follow redirects. Without -f, a 404 page would be piped to bash. Without -L, a CDN redirect would download an empty body.

Timeouts and retries

--max-time SECONDS caps the total request time. --connect-timeout SECONDS caps just the initial connection. Both essential in scripts to avoid hangs.

bash· Linux (GNU)
curl --connect-timeout 5 --max-time 30 https://api.example.com/health

Retry on transient failures:

bash· Linux (GNU)
curl --retry 3 --retry-delay 2 --retry-max-time 30 https://api.example.com/health

By default --retry only retries on transient errors (timeouts, connection refused, some 5xx). Add --retry-all-errors (curl 7.71+) to retry on every failure including 4xx, which is rarely what you want but occasionally useful for stubborn flaky endpoints.

For exponential backoff patterns in shell scripts, the Bash while loop retry pattern gives more control than --retry.

Overriding DNS with --resolve

--resolve HOST:PORT:IP substitutes a DNS lookup for a fixed IP. Useful for testing a new server before DNS cutover, or pinning to a specific origin behind a load balancer.

bash· Linux (GNU)
curl --resolve example.com:443:203.0.113.5 https://example.com

curl sees example.com:443 and uses 203.0.113.5 without hitting DNS, but still sends Host: example.com and validates the TLS cert for example.com. This is the right tool for "is my new server actually serving this hostname before I flip DNS?" tests. Pairs with a DNS health check before and after cutover.

Proxies and --socks5

HTTP/HTTPS proxy:

bash· Linux (GNU)
curl -x http://proxy.internal:8080 https://api.example.com/users

SOCKS5 proxy (matches what an SSH -D 1080 tunnel or Tor's local SOCKS port exposes):

bash· Linux (GNU)
curl --socks5 127.0.0.1:1080 https://example.com

For Tor specifically, the local SOCKS5 port is 9050 by default. See Tor country codes for exit-node selection for how to constrain which exit your traffic flows through, and SOCKS5 proxies with curl, Python, and Node for the equivalent patterns in other languages.

--socks5-hostname (note the suffix) does DNS resolution on the SOCKS side, which is what you almost always want with Tor — otherwise the DNS query leaks to your local resolver:

bash· Linux (GNU)
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org

Useful flag matrix

FlagLong formPurpose
-X--requestHTTP method (GET, POST, PUT, DELETE, ...)
-d--dataRequest body (URL-encoded form by default)
-H--headerAdd or override a header
-u--userBasic auth (or digest with --digest)
-A--user-agentOverride User-Agent
-e--refererSet Referer header
-b--cookieSend a cookie (or read from file with @filename)
-c--cookie-jarWrite received cookies to file
-L--locationFollow redirects
-f--failExit non-zero on HTTP 4xx/5xx
-s--silentNo progress meter or errors
-S--show-errorRe-enable errors (use with -s)
-i--includeInclude response headers in output
-I--headHEAD request (headers only)
-v--verboseTrace request and response
-o--outputSave body to a named file
-O--remote-nameSave body using URL's basename
-J--remote-header-nameUse server's Content-Disposition filename (with -O)
-C--continue-atResume download (-C - for automatic offset)
-k--insecureSkip TLS verification (debugging only)
-x--proxyHTTP proxy
--socks5SOCKS5 proxy
--socks5-hostnameSOCKS5 proxy with remote DNS
--resolveDNS override for a single host
--max-timeTotal request timeout (seconds)
--connect-timeoutInitial connection timeout
--retryNumber of retries on transient failure

Common pitfalls

1. curl URL | bash without -f. A 404 page or a captive-portal HTML gets piped into bash. Always use -fsSL. The -f flag returns non-zero on HTTP error so the pipe never reaches bash.

2. PowerShell aliases curl to Invoke-WebRequest. Running curl --help in PowerShell shows the cmdlet's docs, not curl's. Use curl.exe explicitly in PowerShell. On Windows 10 1803+, the real curl is at C:\Windows\System32\curl.exe.

3. Sending JSON without Content-Type: application/json. curl's -d defaults to application/x-www-form-urlencoded. Many APIs accept the body anyway, but some reject it as malformed form data. Always pair -d with the matching -H 'Content-Type: ...'.

4. Forgetting -X POST is implied by -d. curl -d 'a=1' URL is a POST. Adding -X POST is harmless but redundant. The mistake people make: curl -X GET -d 'a=1' URL sends a GET with a body, which many servers ignore or reject.

5. -L plus auth header. When curl follows a redirect to a different host, it strips the Authorization header by default (a security feature). For cross-host auth survival use --location-trusted. Use sparingly; it leaks credentials to whatever the redirect points at.

6. SOCKS5 DNS leak. --socks5 resolves the hostname locally and sends the IP through the proxy. For Tor or any proxy where you want the destination hostname hidden from your local resolver, use --socks5-hostname.

7. curl exit 0 on 404. Without -f, curl considers any HTTP response a success at the curl level (the network call worked). Scripts that rely on $? to detect API errors must use -f or parse the output.

8. -k in production. -k (or --insecure) skips TLS certificate verification. Useful for testing self-signed certs, dangerous in scripts that run unattended. The right fix is usually --cacert pointing at the actual CA bundle.

What to do next

FAQ

TagscurlHTTPREST APICLIPowerShellDevOpsNetworkingJSON
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts