Short answer: curl URL fetches a URL and prints the body. -I shows only the headers, -L follows redirects, -o FILE saves the response to a file, and -X/-d send a POST with data. Every example below was run against example.com and httpbin.org with curl 7.88.1 (Debian 12); the flags themselves are the same across every curl version in active use.
curl https://example.com # print the response body
curl -I https://example.com # headers only, no body
curl -L https://example.com # follow redirectscurl examples, by task
| Task | Command | Flag |
|---|---|---|
| GET a URL, print the body | curl https://example.com | |
| Headers only, no body | curl -I https://example.com | -I |
| Headers and body together | curl -i https://example.com | -i |
| Follow redirects | curl -L https://example.com | -L |
| Save to a file, keep the original name | curl -O https://example.com/file.zip | -O |
| Save to a file, choose the name | curl -o out.html https://example.com | -o |
| Send a POST with form data | curl -X POST -d "name=vmheaven" https://httpbin.org/post | -X, -d |
| Send JSON | curl -X POST -H "Content-Type: application/json" -d '{"name":"vmheaven"}' https://httpbin.org/post | -H, -d |
| Custom header | curl -H "X-Test: vmheaven" https://httpbin.org/headers | -H |
| Basic auth | curl -u user:pass https://httpbin.org/basic-auth/user/pass | -u |
| Upload a file as form data | curl -F "file=@report.pdf" https://httpbin.org/post | -F |
| Fail on HTTP errors instead of printing the error page | curl -f https://example.com/missing | -f |
| Silent, but still fail loudly on an error | curl -sS https://example.com -o /dev/null | -s -S |
| Only the HTTP status code | curl -s -o /dev/null -w "%{http_code}\n" https://example.com | -w |
| Time the request | curl -s -o /dev/null -w "%{time_total}s\n" https://example.com | -w |
| Resume a partial download | curl -C - -O https://example.com/big.iso | -C - |
POST data: -d vs -F vs JSON
-d sends application/x-www-form-urlencoded data by default — the same encoding an HTML form submits. We ran this against httpbin.org, which echoes back whatever it received:
curl -X POST https://httpbin.org/post -d "name=vmheaven"{
"form": {
"name": "vmheaven"
},
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": "curl/7.88.1"
}
}-X POST is actually redundant here — passing -d switches curl to POST on its own, so curl -d "name=vmheaven" https://httpbin.org/post does the same thing. Writing -X POST explicitly is still common for clarity, and becomes necessary the moment you want a method -d does not imply by itself, such as PUT or DELETE with a body.
For JSON, set the content type yourself — curl does not infer it from the data:
curl -X POST https://httpbin.org/post \
-H "Content-Type: application/json" \
-d '{"name":"vmheaven"}'For a file upload as actual multipart form data (what a file-picker on a web form sends), use -F instead of -d — mixing the two sends the wrong encoding for the kind of data each is meant for:
curl -F "file=@/path/to/report.pdf" https://httpbin.org/postThe leading @ is what tells curl to read a file rather than send the literal string /path/to/report.pdf.
Headers and authentication
curl -H "X-Test: vmheaven" https://httpbin.org/headers{
"headers": {
"User-Agent": "curl/7.88.1",
"X-Test": "vmheaven"
}
}-H is repeatable — pass it once per header. -u user:pass sends HTTP Basic authentication; curl adds the Authorization: Basic ... header itself, base64-encoded, so there is no need to build that header by hand with -H. A failed basic-auth request against httpbin.org’s own test endpoint, which only accepts the exact user/pass pair in its own URL, returns a plain 401:
curl -u user:wrongpass https://httpbin.org/basic-auth/user/pass -w "\nHTTP:%{http_code}\n"HTTP:401Redirects, status codes and -w
By default curl stops at the first redirect response and prints that response’s own body — often a short “moved” page — rather than the page the redirect points to. -L tells it to keep following until it reaches a non-redirect response:
curl -s -o /dev/null -w "%{http_code}\n" http://httpbin.org/redirect/2
# 302 — the redirect response itself, nothing followedcurl -s -L -o /dev/null -w "%{http_code}\n" http://httpbin.org/redirect/2
# 200 — curl followed both hops to the final page-w (--write-out) prints whatever variables you ask for after the transfer finishes, which is the clean way to script against curl’s result instead of parsing output meant for a human. The most useful ones:
| Variable | Meaning |
|---|---|
%{http_code} | The final HTTP status code |
%{url_effective} | The URL actually reached, after redirects |
%{time_total} | Total time for the request, in seconds |
%{time_starttransfer} | Time to first byte of the response |
%{size_download} | Bytes downloaded |
For checking a full redirect chain, status codes and headers without building the curl command yourself, the HTTP header and redirect checker runs the same kind of request from our server in Amsterdam, as a browser, Googlebot or Bingbot.
Debugging a request that fails
| Flag | What it shows |
|---|---|
-v | The full request and response, including the TLS handshake and every header sent and received |
-sS | Silent output, but still print an error message if the request fails — the combination to use in scripts |
-f | Make curl itself return a non-zero exit code on an HTTP 4xx/5xx response, instead of printing the server's error page and exiting 0 |
--resolve HOST:PORT:IP | Force a hostname to a specific IP, bypassing DNS — the standard way to test a server directly before its DNS points there |
-k | Skip TLS certificate verification — for testing an internal or self-signed origin only, never for a production check |
curl: command not found
If none of this works because there is no curl at all, that is a separate, simpler problem — see curl: command not found for the one-line install on every major distribution.
Related tools
- Need the request from a real browser engine, not curl? CORS errors only happen in a browser — curl ignores CORS entirely, which is a common source of “it works in curl but not in the browser” confusion.
- Downloading a whole site, not one file?
wgetis built for recursive downloads in a way curl is not — see wget: command not found if that is missing instead.
Frequently asked
How do I send a POST request with curl?
curl -X POST -d "name=value" URL sends form-encoded data (-d alone already switches curl to POST, so -X POST is optional there but common for clarity). For JSON, add -H "Content-Type: application/json" and pass the JSON string to -d.
How do I make curl follow a redirect?
curl -L URL. Without it, curl stops at the first redirect response and prints that response's own body rather than the page it points to.
How do I see only the HTTP status code with curl?
curl -s -o /dev/null -w "%{http_code}\n" URL — -s silences the normal output, -o /dev/null discards the body, and -w prints just the status code variable afterward.
Why does curl return success for a 404 page?
By default curl's own exit code only reports whether it got a complete HTTP response, not whether that response was an error page. Add -f (--fail) to make curl itself return a non-zero exit code on a 4xx or 5xx response, which is what a script checking $? usually actually wants.