curl Cheatsheet - curl HTTP Debugging Command Reference

For developers who need to hit APIs from the shell, reproduce production issues, and see a full HTTP round trip. Browsers cannot express custom headers, POST bodies, or relaxed TLS checks, so curl is the way to take full control of what a request looks like. By the end you can declaratively build requests with bodies/headers/cookies, break down DNS/connect/TTFB timing with -w to find where slowness lives, and use -v to inspect the TLS handshake and redirect chain.

Web Services·22 commands·Last updated 2026-07-21
curlhttpAPI DebuggingCLI

Typical Use Case

The most typical scenario is "see one real request" while troubleshooting. When the frontend returns 400, replay that exact request with curl -v and inspect the outgoing headers against the server response to tell whether Content-Type is missing or an extra cookie is sent. For intermittent timeouts, use curl -w to split time_namelookup/time_connect/time_appconnect/time_starttransfer and isolate DNS, TCP connection, or slow server response. To reproduce a login-gated issue, carry cookies with curl -b cookies.txt and follow redirects with -L to reach the real landing page. Test a path with -I (headers only) before -d posting data to reduce mistakes.

Basic Requests 6

curl https://example.com
GET request, outputs response body to terminal
curl -X POST https://api.example.com
Specify HTTP method: POST/PUT/DELETE
curl -H "Content-Type: application/json" -H "Authorization: Bearer xxx" https://api.example.com
Add request headers, can stack multiple -H
curl -d '{"k":"v"}' https://api.example.com
Send POST body, default Content-Type is application/x-www-form-urlencoded
curl -d @file.json https://api.example.com
Read body content from file
curl -G https://api.example.com --data-urlencode "q=hello world"
Append params to URL query string with URL encoding

Debugging & Details 6

curl -v https://example.com
Full request/response headers and handshake, first choice for debugging
curl -I https://example.com
Response headers only, quick status and cache check
curl -L https://example.com
Follow 301/302 redirects, use --max-redirs to limit
curl -w "%{http_code} %{time_total}\n" -o /dev/null -s https://example.com
Output only status code and total time, common for monitoring probes
curl -s https://example.com
Silent mode, no progress or errors, pair with -o
curl --resolve example.com:443:1.2.3.4 https://example.com
Override DNS resolution, debug load balancers and certificates

Proxy & Timeout 5

curl -x http://proxy:8080 https://example.com
Use HTTP proxy
curl -x socks5h://127.0.0.1:1080 https://example.com
Use SOCKS5 proxy, h means DNS goes through proxy too
curl --connect-timeout 5 https://example.com
Connection timeout 5s, prevent hanging
curl --max-time 30 https://example.com
Max total time 30s including connect and transfer
curl --retry 3 https://example.com
Auto-retry 3 times on failure

Auth & Upload 5

curl -u user:pass https://example.com
HTTP Basic auth
curl -F "file=@photo.jpg" https://example.com/upload
Upload file as multipart/form-data
curl -F "file=@photo.jpg" -F "name=tom" https://example.com/upload
Upload file with form fields
curl -o photo.jpg https://example.com/photo.jpg
Save response to file, for large downloads
curl --cookie "session=abc" https://example.com
Send cookies, -c writes -b reads cookie file

Parameter matrix

ParameterEffectExample
-X <METHOD>Force the request method, overriding the default curl infers from -dcurl -X POST https://api.example.com
-H <header>Append one header; repeat it to set Content-Type and Authorization among otherscurl -H "Content-Type: application/json" -H "Authorization: Bearer ***" https://api.example.com
-d <data>Send a request body and infer POST; the payload defaults to form encodingcurl -d '{"k":"v"}' https://api.example.com
-d @<file>Read the body from a file, keeping a long payload out of the command linecurl -d @file.json https://api.example.com
-G + --data-urlencode <data>Move the data into the query string with proper URL encoding, so values with spaces need no manual escapingcurl -G https://api.example.com --data-urlencode "q=hello world"
-vPrint the request line, request headers, response headers and handshake; the first tool for debuggingcurl -v https://example.com
-IFetch headers only, confirming status and cache headers without downloading the bodycurl -I https://example.com
-LFollow 301 and 302 hops to the final resource; pair with --max-redirs to cap the chaincurl -L https://example.com
-w <format>Emit custom metrics from a template such as status and timing; monitoring probes rely on it for outputcurl -w "%{http_code} %{time_total}\n" -o /dev/null -s https://example.com
-sSilent mode: drop the progress meter and error chatter so -o downloads leave the terminal cleancurl -s https://example.com
-o <file>Write the response body to a file, replacing terminal output when downloading binary contentcurl -o photo.jpg https://example.com/photo.jpg
--resolve <host:port:ip>Pin a hostname to a specific IP, bypassing DNS caching when debugging load balancing and certificate coveragecurl --resolve example.com:443:1.2.3.4 https://example.com
-x <proxy>Route through the given proxy, for cases where direct access works but proxied access does notcurl -x http://proxy:8080 https://example.com
socks5h://<proxy>Use a SOCKS5 proxy and hand name resolution to it too, avoiding local DNS leakage or poisoningcurl -x socks5h://127.0.0.1:1080 https://example.com
--connect-timeout <sec>Bound only the connection-establishment phase so a stalled handshake cannot hang forevercurl --connect-timeout 5 https://example.com
--max-time <sec>Cap the whole request including connect and transfer, the standard guard against a hung scriptcurl --max-time 30 https://example.com
--retry <n>Retry automatically after failures, absorbing transient network flappingcurl --retry 3 https://example.com
-u <user:pass>Send an HTTP Basic auth header instead of writing Authorization by handcurl -u user:pass https://example.com
--cookie <header>Carry cookies to replay an authenticated request, equivalent to reusing a browser sessioncurl --cookie "session=abc" https://example.com
-F <field=value>Build a multipart/form-data request carrying a file plus ordinary fields at oncecurl -F "file=@photo.jpg" -F "name=tom" https://example.com/upload

Common pitfalls

SymptomA curl call inside a script sits there burning CPU and never returns, holding up the monitoring job.

Causecurl has no timeout by default: as long as the peer keeps the connection open without answering, the call waits indefinitely and the caller cannot tell.

FixSet explicit bounds: --connect-timeout 5 for the connection phase and --max-time 30 for the request as a whole.

SymptomAfter -s the script raises nothing, yet the status it records is always the same failing value.

CauseSilent mode only suppresses terminal output; it does not turn HTTP errors into a non-zero exit code, so shell failure detection never fires.

FixJudge from the response itself: use -w "%{http_code} %{time_total}\n" -o /dev/null -s to pull the status out explicitly and decide for yourself.

SymptomI believed I was testing one hostname and the request actually hit a different machine.

Cause--resolve only overrides local resolution; if another resolution path or a proxy is in play the request may not reach the pinned IP, and the conclusion rests on the wrong backend.

FixAdd -v and read the address actually connected to, confirming it matches the IP in --resolve before drawing conclusions.

SymptomAn upload endpoint answers 4xx and the server says it never received the file.

CauseMixing -F with a hand-written Content-Type collides with the multipart boundary curl generates on its own, mangling the request structure.

FixPick one approach: let -F "file=@photo.jpg" -F "name=tom" generate the multipart headers and do not override Content-Type yourself.

SymptomA large upload is rejected with a malformed request body message.

CauseWith -F curl is responsible for chunking the payload; adding a -d segment concatenates two different body formats into one, which the receiver cannot parse.

FixPut the extra fields in the same -F call, as in -F "file=@photo.jpg" -F "name=tom", and never mix it with -d.

SymptomAn authenticated request returns 401 even though the password is definitely right.

CauseWhen credentials are mixed with -H or -d the generated headers may not match what the server expects, and Basic may not even be the scheme this endpoint uses.

FixEither hand authentication to curl with curl -u user:pass, or state the scheme the endpoint actually wants via curl -H "Authorization: Bearer ***".

SymptomSpaces and special characters in a query value arrive garbled or truncated.

CauseA hand-written URL leaves spaces and characters like & or # unencoded, so the server reads them as separators while parsing the query string.

FixUse curl -G https://api.example.com --data-urlencode "q=hello world" and let curl do the encoding.

SymptomThe same endpoint works when typed by hand but returns an empty response inside a cron job.

CauseA hand-run command can depend on the terminal or the current directory, neither of which exists in a scheduled run, and without -o the output is simply discarded or written elsewhere.

FixRewrite it as curl -s https://example.com with an explicit -o destination, validate the status with -w "%{http_code} %{time_total}\n" -o /dev/null -s, and use absolute paths.

SymptomI set -d and assumed the request was a POST, but the method on the wire was something else.

Cause-X overrides the method curl infers from -d, so a hand-written method that disagrees with the body sends the server down a branch that simply errors out.

FixCheck the request line with -v, and unless you need redirect-specific behaviour, let curl infer the method from -d instead of hardcoding -X.

SymptomAfter a redirect the response is still an interstitial page rather than the target resource.

CauseWithout following, curl hands back the 3xx headers and body verbatim, which is usually just HTML pointing at the next hop.

FixFollow the chain with curl -L https://example.com, and add --max-redirs when you need a cap against redirect loops.

Troubleshooting

  1. 1Confirm the status and cache headers with a headers-only request first

    curl -I https://example.com

    The first line is the status code, and no body is downloaded, which makes this the cheap pre-check before writing to an endpoint.

  2. 2Turn on verbose output when you need the full request, response headers and handshake

    curl -v https://example.com

    This shows the method actually sent, the request headers and the status line, which is what tells you what the server really received.

  3. 3Save both request context and response body to files and read them later

    curl -s https://example.com -o resp.json

    -s keeps the terminal clean and -o puts the body into resp.json; if the file is empty the URL returned no body.

  4. 4Emit only status and total time for a monitoring probe

    curl -w "%{http_code} %{time_total}\n" -o /dev/null -s https://example.com

    The terminal keeps a single line of numbers; time is in seconds, ready to drive timeout and latency alerts.

  5. 5Add the cookie when reproducing an authenticated problem

    curl --cookie "session=abc" https://example.com

    A 200 here means the fault was in authentication; a persistent 401 or 403 points at permissions or the token itself.

  6. 6Pin the hostname to a specific IP when DNS or load balancing is in doubt

    curl --resolve example.com:443:1.2.3.4 https://example.com

    The request goes straight to that IP while keeping the original hostname for TLS and the Host header; a healthy status clears resolution and scheduling as the cause.

  7. 7Bound a probe with timeouts so the job cannot hang

    curl --connect-timeout 5 --max-time 30 --retry 3 https://example.com

    Five seconds to connect, thirty seconds overall, three retries: with all three caps in place the job cannot wait forever.

Command Examples

Break down the time each phase takes in a request

curl -o /dev/null -s -w "DNS: %{time_namelookup}s\nCONNECT: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTOTAL: %{time_total}s\n" https://example.com/api

-o /dev/null drops the body so the timing lines stay readable, and -w prints the per-phase numbers. A slow time_namelookup points at name resolution, a slow time_connect at connectivity, and a slow time_starttransfer at server-side work or a slow first byte.

Output

DNS: 0.012s
CONNECT: 0.047s
TTFB: 0.213s
TOTAL: 0.220s

POST with a JSON body, custom header, and cookie

curl -X POST 'https://api.example.com/login' -H 'Content-Type: application/json' -H 'X-Api-Key: abc123' -b 'session=xyz' -d '{"username":"test"}'

Note that -d does not set the content type on its own, so the header has to be supplied explicitly. -b sends cookies and -H may be repeated to stack several custom headers.

Skip TLS verification for a local self-signed service

curl -k https://localhost:8443/health

Keep -k for local development or an internal self-signed certificate. In production the verification must stay on, because skipping it lets a man-in-the-middle read every byte of the traffic.

Common Pitfalls

  • -d sends form data by default; when POSTing JSON you must add -H "Content-Type: application/json" or the server may parse it as a form and return 400 or empty data.
  • -k disables TLS verification — only acceptable for local/internal self-signed certs. Used on production it defeats HTTPS entirely.
  • When combining -X POST with -d, -X can override the inferred method; confirm the actual method sent with -v.
  • In monitoring scripts -w time_total is measured in seconds and curl has no default timeout; always add --max-time so the process cannot hang forever.
  • To follow redirects use -L rather than editing the URL by hand, or you end up at the redirect page instead of the final resource.

Tips

  • When sending JSON with -d, manually add -H "Content-Type: application/json" or the server may fail to parse.
  • For HTTPS certificate issues, use -v to see the handshake; skip verification temporarily with -k (never in production).
  • -w time_total is in seconds. Monitoring scripts should pair with --max-time to prevent hanging.

FAQ

How do I send a request with a Cookie or a login session in curl?

To pass fixed cookies in one shot, use curl -b "name=value; token=xxx". To store the cookies returned by the server, use -c cookies.txt, then send them back with -b cookies.txt to reuse the session. For login flows, run the login request with -c first, then use -b on the following requests to stay authenticated.

How do I get a curl command for an authenticated API from the browser?

Open DevTools with F12, go to the Network panel, perform the authenticated request once, then right-click it and choose Copy as cURL. That reproduces all the headers, cookies, and body. Sanitize any exposed Cookie or Token in that command before reuse or sharing.

How do I POST JSON vs form-encoded data with curl?

For JSON, use curl -X POST -H "Content-Type: application/json" -d '{"a":1}'. For form data, -d "key=value&k2=v2" sets Content-Type to application/x-www-form-urlencoded automatically, or use -F "file=@a.png" for multipart uploads. Note -X POST is not needed when -d is present, since curl implies it.

What should I do when curl reports an HTTPS certificate error?

For self-signed or internal certificates, pass -k (--insecure) to skip verification while diagnosing; in production, prefer fixing the CA or adding the proper certificate rather than relying on -k. To trust a specific CA, use --cacert path.pem. Add --noproxy "*" to bypass a proxy if one is interfering.

How do I tell whether slowness is in the network or the server?

Use curl -w "\ntime_namelookup:%{time_namelookup}\ntime_connect:%{time_connect}\ntime_starttransfer:%{time_starttransfer}\ntime_total:%{time_total}\n" -o /dev/null -s URL. time_connect reflects TCP handshake and network latency, time_starttransfer is the time to the first byte (server processing plus TTFB), and time_total is the total. Comparing the phases tells you which segment is slow. A short form is curl -w '%{time_total}\n' -o /dev/null -s URL.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us