Terminal · foundation

HTTP requests with curl

curl is a command-line data-transfer client that uses a URL to select a protocol and exposes options for constructing requests, inspecting transfers, handling failures, and writing response data. For HTTP, its defaults and options map to request methods, fields, content, redirects, and authentication.

Why it matters

A carefully written curl command is a reproducible probe across DNS, connection, TLS, HTTP, and application layers. Explicit time limits, failure rules, and output handling turn an exploratory request into a dependable diagnostic or automation step.

Mental model

How to reason about hTTP requests with curl

Read a curl command as four decisions: construct the request, control transfer behavior, choose what to print or save, and define failure. Keep curl's process exit status, the HTTP response status, headers, and response body separate. Options such as -i change output, while -I changes the HTTP request itself.

Analogy

curl is a configurable test courier. You specify the destination, envelope fields, payload, delivery limits, which parts of the receipt to retain, and which transport or response outcomes should count as failure.

Examples

See the boundary, not just the happy path

Worked example · Fetch JSON and fail on HTTP errors

curl --fail-with-body --silent --show-error --connect-timeout 5 --max-time 15 -H 'Accept: application/json' https://api.example.com/v1/status

The request advertises JSON acceptance and bounds connection setup and total transfer time. --fail-with-body makes HTTP 400 or greater produce curl error 22 while retaining the response body; --show-error preserves diagnostics in silent mode.

Worked example · POST a JSON representation

curl --fail-with-body -H 'Content-Type: application/json' --data '{"name":"Ada"}' https://api.example.com/v1/users

--data supplies request content and makes curl use POST unless another method is selected. Content-Type describes the representation being sent; it does not ask for a JSON response, which would use Accept.

Worked example · Encode query parameters

curl --get --data-urlencode 'q=retry & backoff' --data-urlencode 'limit=10' https://api.example.com/search

--get moves the encoded data into the URL query instead of a request body. --data-urlencode protects spaces and the ampersand inside the q value from being misread as query syntax.

Worked example · Save the body and report status

curl --silent --show-error --output response.json --write-out 'HTTP %{response_code}\n' https://api.example.com/v1/status

The response body goes to response.json while --write-out prints the final HTTP status. A response code of 000 can mean no HTTP response arrived; always inspect curl's separate process exit status as well.

Useful contrast · Response headers versus a HEAD request

curl -i https://example.com/resource
curl -I https://example.com/resource

-i keeps the normal GET and includes response headers with the body. -I asks the server for headers using HTTP HEAD and does not download the response body.

Avoid · Execute an unreviewed download

curl -fsSL https://unknown.example/install.sh | sh

Piping a network response directly into a shell removes the opportunity to inspect, authenticate independently, checksum, or archive the exact code before execution. Download and verify first.

Common mistakes

Misconceptions to remove early

Assuming HTTP 500 makes curl fail by default

A completed HTTP transfer normally gives curl exit status zero even for an HTTP error response. Use --fail or --fail-with-body when scripts should treat applicable 4xx/5xx statuses as failures.

Forcing a method without matching request semantics

-X changes the method token but does not configure a matching body or transfer behavior. Prefer semantic options such as --data or --json, --head, --form, and --upload-file when they express the intended operation.

Confusing -i with -I

Lowercase -i includes response headers in the output without changing the method. Uppercase -I requests headers with HTTP HEAD, which can follow different server logic than a normal GET.

Assuming redirects are followed automatically

curl reports a redirect response but does not follow it unless requested. Use --location deliberately, consider --max-redirs, and review where credentials or request bodies may be sent.

Leaving automated requests without time limits

A connect timeout covers connection establishment, while --max-time limits the whole transfer. Set both from the caller's latency budget so a stalled dependency cannot hold a worker indefinitely.

Using --insecure as a TLS repair

--insecure suppresses certificate and hostname verification; it does not fix an expired certificate, wrong host name, or missing trust chain. Diagnose and repair the trust problem instead.

Quick check

Can you predict the result?

1. Why add --fail-with-body to curl in an automated check?
  • It makes applicable HTTP error statuses produce a nonzero curl exit while preserving the response body.
  • It disables TLS certificate validation.
  • It retries every request until it receives HTTP 200.
Answer: It makes applicable HTTP error statuses produce a nonzero curl exit while preserving the response body.
2. What is the difference between Content-Type and Accept in an HTTP request?
Answer: Content-Type describes the request content being sent; Accept expresses preferred response media types.
3. What is the difference between curl -i and curl -I for an HTTP URL?
Answer: -i includes response headers with the normal response body; -I sends a HEAD request and fetches headers without the body.
4. Does plain curl normally return a nonzero process status for an HTTP 404 response?
Answer: No. If the HTTP transfer completes, curl normally returns zero; use --fail or --fail-with-body when applicable HTTP error statuses should make the command fail.

Keep building

Put the concept to work

Practical guides that use this mental model

Authoritative references

Make the idea retrievable.

Concepts are coming to Terminaster in the next update. You'll be able to study this one with spaced repetition, next to the commands where you use it.

Get Terminaster