| Tested on | Ubuntu 25.04 (Plucky Puffin) |
|---|---|
| Package | curl 8.12.1curl 8.12.1 |
| Applies to | Ubuntu, Debian, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux, SUSE, openSUSE, Alpine |
| Privilege | sudo or root |
| Man page | curl(1) |
| Scope | curl transfers data over HTTP, HTTPS, FTP, and other protocols from the shell. Use it to fetch URLs, inspect headers, download files, and send POST and JSON payloads when testing APIs or debugging connectivity. |
| Related guides | Install curl on Ubuntu grep Linux commands |
curl — quick reference
Fetch and save
Transfer URL content to stdout or to a file. Examples use https://example.com — a safe documentation domain.
| When to use | Command |
|---|---|
| Print response body to the terminal | curl -s --connect-timeout 5 -m 10 https://example.com |
| Save under a chosen filename | curl -s -o page.html --connect-timeout 5 -m 10 https://example.com |
| Save using the remote filename from the URL | curl -s -O --connect-timeout 5 -m 10 https://example.com/index.html |
| Redirect body to a file with shell redirection | curl -s --connect-timeout 5 -m 10 https://example.com > page.html |
HTTP inspection and redirects
| When to use | Command |
|---|---|
Fetch response headers only (HEAD) |
curl -sI --connect-timeout 5 -m 10 https://example.com |
| Include response headers with the body | curl -si --connect-timeout 5 -m 10 https://example.com |
| Print only the HTTP status code | curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 -m 10 https://example.com |
| Follow redirects to the final URL | curl -sL --connect-timeout 5 -m 10 https://example.com |
Script-friendly behavior
| When to use | Command |
|---|---|
| Silent mode — no progress meter | curl -s URL |
| Fail silently on HTTP 4xx/5xx (non-zero exit) | curl -sf URL |
Show errors even when -s is set |
curl -sS URL |
POST, JSON, and custom headers
Use httpbin.org for safe POST echo tests — it reflects what you sent.
| When to use | Command |
|---|---|
| POST form fields | curl -s -d 'key=value' --connect-timeout 5 -m 20 https://httpbin.org/post |
| POST JSON with a Content-Type header | curl -s -H 'Content-Type: application/json' -d '{"key":"value"}' https://httpbin.org/post |
| Send a custom request header | curl -s -H 'X-Test: demo' https://httpbin.org/headers |
Explicit POST method (when not implied by -d) |
curl -s -X POST -d 'a=1' https://httpbin.org/post |
Authentication, TLS, and debugging
| When to use | Command |
|---|---|
| HTTP basic authentication | curl -s -u user:pass https://example.com |
| Verbose connection and TLS trace | curl -sv --connect-timeout 5 -m 10 https://example.com |
| Skip TLS certificate verification (lab only) | curl -sk https://self-signed.example |
Timeouts and local testing
| When to use | Command |
|---|---|
| Limit time to connect (seconds) | curl --connect-timeout 5 https://example.com |
| Limit total transfer time (seconds) | curl -m 30 https://example.com |
| Hit a local service without a proxy | curl -s --noproxy '*' http://127.0.0.1:8080/ |
Help and version
| When to use | Command |
|---|---|
| Show common options | curl --help |
| List all options | curl --help all |
| Show curl and libcurl version | curl -V |
curl — command syntax
Synopsis from curl --help on Ubuntu 25.04 (curl 8.12.1):
curl [options...] <url>curl writes the response body to stdout by default. Use -o or -O to save to a file. Combine -s with -w for script-friendly status checks. On hosts with an HTTP proxy in the environment, add --noproxy '*' when testing 127.0.0.1 locally.
curl — command examples
Essential Fetch a URL and print the body
The simplest GET request prints HTML or API output to the terminal. -s hides the progress bar; timeouts keep accidental hangs short.
Run the command:
curl -s --connect-timeout 5 -m 10 https://example.com | head -c 200Sample output (truncated):
<!doctype html><html lang="en"><head><title>Example Domain</title><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{background:#eee;width:6Pipe to grep, jq, or a file when you need structured processing.
Essential Response headers with -I
-I sends a HEAD request — headers only, no body. Use it to check status, Content-Type, and cache headers quickly.
Run the command:
curl -sI --connect-timeout 5 -m 10 https://example.comSample output:
HTTP/2 200
date: Wed, 01 Jul 2026 12:23:09 GMT
content-type: text/html
server: cloudflare
last-modified: Tue, 30 Jun 2026 20:56:01 GMT
allow: GET, HEAD
accept-ranges: bytesThe first line is the HTTP status — 200 means the resource is reachable.
Essential Download to a chosen filename with -o
-o writes the response to a path you specify — good when the URL filename is ugly or missing.
Run the command:
curl -s -o /tmp/example-page.html --connect-timeout 5 -m 10 https://example.com
wc -c /tmp/example-page.html
rm -f /tmp/example-page.htmlSample output:
559 /tmp/example-page.htmlUse -O instead when you want the last path segment of the URL as the local name.
Common Print only the HTTP status code
-w (write-out) formats metadata after the transfer. Send the body to /dev/null when you only care about the code.
Run the command:
curl -s -o /dev/null -w '%{http_code}\n' --connect-timeout 5 -m 10 https://example.comSample output:
200In scripts, combine with -f to treat 4xx/5xx as failure without parsing text output.
Common Follow redirects with -L
curl does not follow redirects by default. -L (or --location) requests each redirect target until a final response.
Run the command:
curl -sI -L --connect-timeout 5 -m 10 http://example.com | grep -E '^HTTP' | head -3Sample output:
HTTP/1.1 200 OKWithout -L, you might stop at a 301/302 with an empty body — add -L when the URL chain matters.
Common POST form data with -d
-d sends a POST body. By default curl uses application/x-www-form-urlencoded. httpbin echoes the form for testing.
Run the command:
curl -s --noproxy '*' -d 'name=test&role=demo' --connect-timeout 5 -m 20 https://httpbin.org/post | grep -A3 '"form"'Sample output:
"form": {
"name": "test",
"role": "demo"
},Replace the URL with your API endpoint in production; keep httpbin for labs and CI smoke tests.
Common POST JSON with a Content-Type header
APIs expect Content-Type: application/json when the body is JSON. Quote the payload carefully in the shell.
Run the command:
curl -s --noproxy '*' -H 'Content-Type: application/json' \
-d '{"key":"value"}' --connect-timeout 5 -m 20 https://httpbin.org/post | grep -E '"json"|"data"' | head -4Sample output:
"data": "{\"key\":\"value\"}",
"json": {
"key": "value"
},httpbin parses JSON when the header is set — your API may return a different shape; pipe to jq when available.
Common Send a custom header
Repeat -H for multiple headers — tokens, correlation IDs, or Accept negotiation.
Run the command:
curl -s --noproxy '*' -H 'X-Test: demo' --connect-timeout 5 -m 20 https://httpbin.org/headers | grep X-TestSample output:
"X-Test": "demo"Never paste production secrets into shell history — use environment variables or a netrc file for credentials.
Advanced Debug connection and TLS with -v
-v traces resolution, TCP, TLS handshake, and request headers on stderr — first stop for HTTPS failures.
Run the command:
curl -sv --noproxy '*' --connect-timeout 5 -m 10 https://example.com 2>&1 | head -18Sample output:
* Host example.com:443 was resolved.
* IPv6: (none)
* IPv4: 172.66.147.243, 104.20.23.154
* Trying 172.66.147.243:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):Look for certificate errors, proxy CONNECT lines, and which IP address was tried.
Advanced Fail on HTTP errors with -f
-f (--fail) makes curl exit non-zero on HTTP 4xx/5xx instead of printing an error page body — useful in scripts.
Run the command:
curl -sf --noproxy '*' --connect-timeout 5 -m 10 -o /dev/null -w '%{http_code}\n' https://example.com/not-found
echo exit:$?Sample output:
404
exit:22Exit code 22 means HTTP error with -f. A successful 200 response returns exit 0.
curl — when to use / when not
| Use curl when | Use something else when |
|---|---|
|
curl vs wget
| curl | wget | |
|---|---|---|
| Primary strength | APIs, headers, many protocols, upload | Non-interactive downloads and mirroring |
| Output default | stdout | Saves to file |
| Recursive crawl | No | Yes (-r, -p) |
| REST testing | Excellent (-H, -d, -X) |
Possible but less ergonomic |
| Typical use | CI checks, API calls, debugging | Bulk downloads, static site mirrors |
Both are standard on Linux — curl for HTTP semantics, wget for pull-down and mirror jobs.
curl — interview corner
What is curl used for?
curl is a command-line client for transferring data with URLs. It speaks HTTP, HTTPS, FTP, SFTP, and many other protocols via libcurl.
Admins and developers use it to download files, probe APIs, inspect headers, and debug TLS — especially in scripts where a GUI browser is unavailable.
A strong answer is:
"curl transfers data from URLs — mostly HTTP/HTTPS for API tests, downloads, and header checks. It's script-friendly and supports dozens of protocols through libcurl."
What is the difference between curl and wget?
wget excels at non-interactive downloads and recursive mirroring — -O, -r, -p.
curl excels at HTTP control — custom methods and headers, -w write-out, uploads, and multi-protocol clients.
Rule of thumb: wget to pull files; curl to talk to APIs and inspect HTTP behavior.
A strong answer is:
"wget for mirroring and simple downloads; curl for APIs, headers, POST bodies, and protocol flexibility."
Does curl follow redirects by default?
No. Without -L, curl returns the redirect response (for example 301/302) and does not fetch the final URL automatically.
Add -L (--location) to follow redirects — common when a short URL expands to the real endpoint.
A strong answer is:
"Redirects are off by default — I add -L when I need the final URL after HTTP redirects."
How do you use curl in shell scripts?
Common pattern:
code=$(curl -sf -o /dev/null -w '%{http_code}' --connect-timeout 5 -m 30 https://example.com)-s— no progress meter-f— fail on HTTP errors-w— print status code-o /dev/null— discard body when only the code matters
Add -S to show errors while staying silent on success output.
A strong answer is:
"I use curl -sf with -w for status codes and timeouts, often discarding the body to /dev/null when checking health endpoints."
When is curl -k appropriate?
-k (--insecure) skips TLS certificate verification. It is only for local labs with self-signed certs — never as a permanent fix on production traffic.
Fix the trust chain (install CA, use Let's Encrypt, correct hostname) instead of leaving -k in scripts.
A strong answer is:
"-k bypasses cert verification — okay briefly in a dev lab with self-signed certs; in production I fix CA trust instead."
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Could not resolve host |
DNS failure or typo | Check URL spelling; dig hostname |
Connection timed out |
Firewall, wrong port, or dead host | Verify service listens; add --connect-timeout |
| TLS / certificate errors | Expired cert, hostname mismatch, missing CA | curl -v for details; fix server cert — avoid -k in prod |
Empty body but 301/302 |
Redirect not followed | Add -L |
| Localhost hits a proxy | http_proxy / https_proxy set |
curl --noproxy '*' http://127.0.0.1:PORT/ |
| HTTP 200 but script fails | Missing -f conflated with success body |
Use -f and check exit code; -w '%{http_code}' |
curl: command not found |
Package missing | sudo apt install curl |
