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 Karunaratne⏱️ 13 min readUpdated
Share thisCopied
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

Pick your OS. Every command on this page updates to the variant that works on your platform.

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

curl and wget are the right tools for files and API responses, but they cannot pull video or audio from sites like YouTube, which serve media as separate, often DRM-fronted streams. For that, reach for yt-dlp instead: see how to download a YouTube video.

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

Sources

Authoritative references this article was fact-checked against.

TagscurlHTTPREST APICLIPowerShellDevOpsNetworkingJSON

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts

Every HTTP request field that carries SQL injection: URL, body, User-Agent, Referer, Cookie, X-Forwarded-For, Host, Authorization, custom headers.

SQL Injection in HTTP Requests: Every Vector Attackers Use

A practical map of every place SQL injection can live inside an HTTP request: query string, URL path, request body (form, JSON, XML, multipart filename), and every header from User-Agent to Authorization. Where attackers look, why developers miss each one, and where to start hardening.