GET Requests
| Command | What it does |
|---|---|
| GET and print the body | |
| Follow redirects | |
| Silent: no progress meter | |
| Silent, but still print errors | |
| Fail with exit code 22 on HTTP 4xx and 5xx instead of printing the error page | |
| The combination scripts want | |
| Ask for JSON | |
| Set the User-Agent | |
| Set the Referer | |
| Send a cookie | |
| Save cookies from the response to a file | |
| Send cookies from that file on the next call | |
| Ask for gzip and decode it | |
| Pretty-print JSON with jq | |
| Several URLs in one command |
POST and JSON
implies POST and sets , so JSON needs the header.
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"widget","qty":1}'| Command | What it does |
|---|---|
| Form POST (what an HTML form sends) | |
| JSON body from a file | |
| curl 7.82 and newer: sets Content-Type and Accept for you | |
| Same, from a file | |
| PUT | |
| PATCH | |
| DELETE | |
| POST with an empty body | |
| Send a value starting with without reading a file | |
| Send bytes exactly as they are ( strips newlines) |
Headers
| Command | What it does |
|---|---|
| Add a header | |
| Several headers: repeat | |
| Test a virtual host before DNS points at it | |
| Pin a hostname to an IP; keeps TLS and SNI correct, unlike the Host trick | |
| Remove a default header (name with an empty value) | |
| Send a header with an empty value (trailing semicolon) | |
| Headers from a file, one per line (curl 7.55 and newer) | |
| Fetch compressed without decoding |
Authentication
| Command | What it does |
|---|---|
| Bearer token | |
| Same, shorter | |
| Basic auth | |
| Basic auth, prompt for the password so it stays out of history | |
| API key in a header | |
| API key as a query parameter (quote the URL) | |
| Digest auth | |
| NTLM, for IIS and Windows proxies | |
| Credentials from () | |
| Client certificate (mutual TLS) |
Downloads
| Command | What it does |
|---|---|
| Save with the file name from the URL | |
| Save under a name you choose | |
| Follow redirects, then save (GitHub release assets) | |
| Use the name from the header | |
| Resume a partial download | |
| Progress bar instead of the meter | |
| Several files | |
| Numeric range | |
| Create the target directories | |
| Throttle to 1 MB/s | |
| Only download if newer than the local copy | |
| Discard the body (timing, status checks) | |
| The install-script pattern; read the script first |
Uploads
| Command | What it does |
|---|---|
| multipart/form-data upload, what a browser file field sends | |
| Set the MIME type and add a text field | |
| Upload under a different name | |
| PUT the file as the raw request body | |
| Trailing slash: PUT to the URL plus the file name | |
| Several files | |
| FTP upload | |
| SFTP upload (curl built with libssh2; lists sftp if so) | |
| Raw bytes in a POST |
Response Headers and Status Codes
| Command | What it does |
|---|---|
| HEAD request: headers only | |
| Headers followed by the body | |
| Headers of a real GET, for servers that treat HEAD differently | |
| Save the headers to a file | |
| Just the status code | |
| Final code and URL after redirects | |
| Total time in seconds | |
| Which address it actually connected to | |
| Bytes received | |
| Read the format from a file |
Where the time goes:
curl -s -o /dev/null -w "dns %{time_namelookup}s connect %{time_connect}s tls %{time_appconnect}s first byte %{time_starttransfer}s total %{time_total}s code %{http_code}\n" https://example.comVerbose and Debugging
| Command | What it does |
|---|---|
| Show the request (), response headers (), and connection and TLS details () | |
| Verbose without the body in the way | |
| Only the headers from the verbose output | |
| Everything on the wire, including bodies | |
| Same, to a file with timestamps | |
| Exit 22 on HTTP errors but still print the body (curl 7.76 and newer) | |
| Force HTTP/1.1 (, for the others) | |
| Force IPv4 ( for IPv6) | |
| Version, TLS backend, and supported protocols | |
| Print the exit code and error text (curl 7.75 and newer) |
Certificates and -k
| Command | What it does |
|---|---|
| Skip certificate verification (); for testing, not for scripts | |
| Trust a private CA and keep verification on | |
| Use a directory of CA certificates | |
| Certificate chain, issuer, and dates in the lines | |
| 0 means the chain verified | |
| Require a stapled OCSP response | |
| Minimum TLS 1.2 ( caps it, for testing old servers) | |
| Windows only: skip the revocation check that fails behind some proxies | |
| Mutual TLS with a private CA |
Exit code 60 means the chain did not verify. The SSL checker shows what the server is sending, including a missing intermediate, and the certificate decoder reads a you already have.
Proxies
| Command | What it does |
|---|---|
| HTTP proxy | |
| Proxy with credentials ( also works) | |
| SOCKS5 proxy that also resolves DNS; pairs with from the SSH cheat sheet | |
| SOCKS5 with local DNS | |
| Ignore the proxy environment variables | |
| Tunnel through the proxy with CONNECT () | |
| Environment variables curl honours (, too) |
prints when one of those variables is set, which explains many mystery failures.
Query Parameters
| Command | What it does |
|---|---|
| Quote URLs with , or the shell backgrounds the command | |
| Encode a value and send it as a query string | |
| Build a query string from pairs instead of a POST body | |
| Same as , curl 7.87 and newer | |
| URL-encode a POST field | |
| URL-encode a file's contents into a field | |
| Brace expansion fetches three URLs | |
| Turn globbing off so and are sent literally |
Timeouts and Retries
| Command | What it does |
|---|---|
| Give up on connecting after 5 seconds | |
| Cap the whole transfer at 10 seconds (exit code 28) | |
| Retry transient failures (timeouts, 5xx) up to 3 times | |
| Wait 2 seconds between retries instead of backing off | |
| Retry on every error, including connection refused | |
| Treat connection refused as transient | |
| Abort if slower than 1000 bytes/s for 30 seconds | |
| TCP keepalive probe interval ( turns it off) | |
| No output buffering; use for server-sent events and streaming logs |
Connectivity Checks
| Command | What it does |
|---|---|
| Is the site answering | |
| Is a TCP port open: look for (Ctrl+C to leave) | |
| Quick port probe over HTTP; an SSH banner or means the port is open | |
| Which IP the name resolved to | |
| Test a new server before changing DNS | |
| Send from a specific local address or interface | |
| Your public IPv4 address | |
| Headers and status without the body |
For a port you cannot reach from your machine, the SSH port checker tests it from outside your network.
Exit Codes
| Code | Meaning | Usual cause |
|---|---|---|
| Success | The transfer finished; HTTP 500 still counts unless you used | |
| Unsupported protocol | Typo like , or a build without that protocol | |
| Malformed URL | Unquoted spaces or brackets | |
| Could not resolve proxy | Bad or proxy environment variable | |
| Could not resolve host | DNS is down, the name is wrong, or there is no network | |
| Failed to connect | Port closed, service down, or a firewall refused it | |
| Partial file | The connection dropped mid-download; retry with | |
| HTTP error | A 4xx or 5xx with | |
| Write error | Disk full, or no permission on the path | |
| Read error | The , , or file is missing or unreadable | |
| Timeout | or hit, often a silent firewall | |
| TLS handshake failed | HTTPS to an HTTP port, protocol or cipher mismatch, Windows revocation check | |
| Too many redirects | A redirect loop with ( caps it) | |
| Empty reply | The server closed the connection without responding | |
| Send failure | Connection dropped while uploading | |
| Receive failure | Connection reset by the server, proxy, or load balancer | |
| Certificate not verified | Self-signed, expired, private CA, missing intermediate, wrong clock | |
| CA bundle problem | Bad path or a missing system CA store |
shows the code right after curl exits. Codes are documented in under EXIT CODES.
Windows and PowerShell
| Situation | Do this |
|---|---|
| Windows PowerShell 5.1 | is an alias for ; type to get real curl |
| PowerShell 7 | The alias is gone; runs curl.exe |
| cmd.exe | is real curl on Windows 10 1803 and later |
| JSON in cmd.exe | No single quotes: |
| JSON in PowerShell 7.3 and newer | works as in bash |
| JSON in older PowerShell | Inner quotes get stripped; write |
| Line continuation | in cmd.exe, a backtick in PowerShell, not |
| Certificate errors (exit 35 or 60) | Windows curl uses Schannel and the Windows certificate store; if the revocation check fails behind a proxy |
| Git Bash | Ships its own curl with OpenSSL and its own CA bundle, so results can differ from cmd.exe |
Gotchas
- already means POST. Adding is harmless alone, but with forces POST on the redirect target too, where curl would normally switch to GET.
- sends HEAD, and some servers answer HEAD with 405 or different headers. shows the headers of a real GET.
- Quote every URL that contains , , , , or . Unquoted, the shell backgrounds the command at and curl globs the brackets.
- strips newlines from the file. sends it byte for byte.
- makes the error go away, not the problem. Find the cause with the SSL checker and fix the chain or add .
- is visible in shell history and . Use to be prompted, or with a .
- overwrites an existing file without asking, and lets the server choose the file name.
- A that works in the terminal but not in cron or CI is usually missing a proxy variable, a , or the CA bundle the shell had.