curl Cheatsheet
Last updated 11th May 2026 Reviewed May 2026

curl Cheatsheet

What it is

curl is the universal HTTP client for the command line. In a deployment context it's the glue between every other tool: triggering deploy webhooks, smoke-testing endpoints after a release, looping on healthchecks, asserting TLS renewals, and measuring response timing for SLO checks. Most of what makes curl deploy-grade isn't the request syntax — it's the exit-code discipline (--fail), the timeouts (--max-time), and the retry policy (--retry-all-errors) that decides whether your CI job correctly fails on a half-broken release.

This cheatsheet skips the trivia and focuses on the flags you reach for during a deploy session.

Quick reference

Basic requests

curl https://api.example.com/health             # GET (default)
curl -I https://api.example.com/health          # HEAD only — fast healthcheck
curl -X POST https://api.example.com/builds     # explicit method
curl -X DELETE https://api.example.com/builds/42

# Follow redirects (POST stays POST)
curl -L --post301 --post302 -X POST https://api.example.com/deploys

Headers and auth

curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me
curl -H "X-Deploy-Signature: $SIG"     https://hooks.example.com/deploy
curl -u user:pass                      https://api.example.com/private    # basic auth
curl --user-agent "deploy-bot/1.0"     https://api.example.com/build
curl -A "deploy-bot/1.0"               https://api.example.com/build      # alias for --user-agent

Output and inspection

curl -s   https://api.example.com         # silent (no progress bar)
curl -sS  https://api.example.com         # silent BUT show errors — use this in scripts
curl -i   https://api.example.com         # include response headers in body
curl -I   https://api.example.com         # ONLY response headers (HEAD)
curl -v   https://api.example.com         # verbose — TLS handshake + headers + body
curl -o response.json    https://...      # write body to file
curl -O                  https://.../file.tar.gz   # save with remote filename
curl -w "%{http_code} %{time_total}\n" -o /dev/null -s https://api.example.com

JSON, form data, file uploads

# JSON (curl 7.82+) — sets Content-Type and Accept automatically
curl --json '{"tag":"v1.4.2"}' https://api.example.com/deploys

# JSON the manual way
curl -H "Content-Type: application/json" -d '{"tag":"v1.4.2"}' https://api.example.com/deploys

# JSON from file
curl --json @payload.json https://api.example.com/deploys

# URL-encoded form data
curl -d "key=value" -d "tag=v1.4.2" https://hooks.example.com/deploy
curl --data-urlencode "comment=Released $(git rev-parse --short HEAD)" https://...

# Multipart upload
curl -F "file=@release.tar.gz" -F "sha=$SHA" https://api.example.com/artifacts

# Raw upload (PUT a file)
curl -T release.tar.gz https://api.example.com/artifacts/release.tar.gz

Timing, retries, timeouts

curl --connect-timeout 5 --max-time 30 https://api.example.com   # never hang
curl --retry 3 --retry-delay 2 https://flaky.example.com         # retry transient errors
curl --retry 3 --retry-all-errors https://flaky.example.com      # retry on ALL failures (curl 7.71+)
curl --retry 5 --retry-max-time 60 https://flaky.example.com     # cap total retry window

# Detailed timing (great for SLO assertions)
curl -o /dev/null -s -w "DNS:%{time_namelookup} CONN:%{time_connect} TLS:%{time_appconnect} TTFB:%{time_starttransfer} TOTAL:%{time_total}\n" https://api.example.com

TLS, certs, cookies

curl --cacert ca.pem      https://...           # trust a custom CA
curl --cert client.pem --key client.key https://mtls.example.com   # mTLS
curl --tlsv1.3            https://...           # force TLS 1.3 minimum
curl --resolve api.example.com:443:10.0.0.5 https://api.example.com  # DNS override

# Cookies
curl -c cookies.txt https://login.example.com/auth   # save cookies
curl -b cookies.txt https://api.example.com/me       # send cookies

-k / --insecure skips TLS verification. Don't use it in production deploys — fix the cert chain instead.

Deployment workflows (the moat)

1. Trigger a DeployHQ deploy via webhook

Most CI providers can hit a webhook to kick off a DeployHQ deploy after the build completes. Pattern:

#!/usr/bin/env bash
set -euo pipefail

WEBHOOK_URL="${DEPLOYHQ_WEBHOOK_URL:?missing}"
SHA="$(git rev-parse HEAD)"
BRANCH="$(git rev-parse --abbrev-ref HEAD)"

curl -fsS \
  --max-time 30 \
  --retry 3 --retry-delay 5 --retry-all-errors \
  -H "Content-Type: application/json" \
  --data-urlencode "sha=${SHA}" \
  --data-urlencode "branch=${BRANCH}" \
  "$WEBHOOK_URL"

Three deploy-grade flags carrying the weight:

  • -f / --fail — exit non-zero on HTTP ≥ 400 (without it, curl returns 0 even on a 500)
  • --max-time 30 — never hang the CI runner
  • --retry-all-errors — retry through transient network blips, not just curl's own errors

DeployHQ's automatic deployments accept this exact webhook shape.

2. Smoke-test the release after deploy

The most common CI bug: tests "pass" because curl returns 0 even when the staging URL serves an HTTP 502. Fix:

curl --fail-with-body \
     --max-time 10 \
     --retry 3 --retry-delay 5 --retry-all-errors \
     "https://staging.example.com/healthz"

--fail-with-body (curl 7.76+) is the version of --fail that prints the response body on error — so you actually get the panic message, not a silent exit code 22. Make this the first step after the DeployHQ webhook completes — if the new release isn't healthy, fail loudly.

3. Wait-for-200 healthcheck loop

Right after a zero-downtime swap, the new instances may take a few seconds to register. Wait for them rather than racing the next step:

#!/usr/bin/env bash
set -euo pipefail
URL="${1:?healthcheck URL required}"
DEADLINE=$((SECONDS + 120))

until curl -fsS --max-time 5 -o /dev/null "$URL"; do
  [[ $SECONDS -lt $DEADLINE ]] || { echo "Healthcheck timeout: $URL" >&2; exit 1; }
  sleep 2
done
echo "Healthy: $URL"

Pair with DeployHQ's zero-downtime deployments — the swap is atomic, but DNS and load-balancer registration aren't. The loop closes that gap.

4. Assert response timing (SLO smoke check)

If your post-deploy SLO is "TTFB under 500ms from a fresh container", encode it:

TTFB_MS=$(curl -o /dev/null -sS \
  -w "%{time_starttransfer}" \
  --max-time 5 \
  "https://api.example.com/healthz" \
  | awk '{printf "%d", $1 * 1000}')

if (( TTFB_MS > 500 )); then
  echo "TTFB regression: ${TTFB_MS}ms (budget 500ms)" >&2
  exit 1
fi

Drop this into a build pipeline step and the deploy fails the moment a release crosses the SLO budget.

5. Verify TLS chain after a cert renewal

After Let's Encrypt rotates a cert, confirm the served chain matches expectations before the next deploy:

curl -vI --max-time 10 https://example.com 2>&1 \
  | grep -E "expire date|subject|issuer|TLSv1\.[23]"

For a hard assertion (bail if cert expires within 30 days):

END=$(curl -vI https://example.com 2>&1 | sed -n 's/.*expire date: //p')
[[ -n "$END" ]] || { echo "no expiry parsed" >&2; exit 1; }
END_TS=$(date -d "$END" +%s)
NOW_TS=$(date +%s)
DAYS=$(( (END_TS - NOW_TS) / 86400 ))
(( DAYS > 30 )) || { echo "Cert expires in $DAYS days" >&2; exit 1; }

6. Verify a webhook signature you just received

When DeployHQ (or any provider) signs outbound webhooks with HMAC-SHA256, the verification side uses curl-adjacent shell tooling rather than curl itself, but the receiver script is part of every deploy hook:

# Inside your webhook receiver
EXPECTED=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -binary | base64)
[[ "$EXPECTED" == "${HTTP_X_HUB_SIGNATURE_256#sha256=}" ]] \
  || { echo "Invalid signature" >&2; exit 1; }

The --fail discipline applies on the sending side — when curl POSTs the webhook, exit non-zero on a 4xx/5xx so the sender retries cleanly. See the agent-driven CI/CD walkthrough for the full webhook-sender + receiver pattern in a real deploy pipeline.

Common errors and fixes

Symptom Fix
curl returns exit code 0 on HTTP 500 Use -f / --fail (or --fail-with-body for the response body too)
Auth header is dropped after a redirect --location-trusted only if you understand the cross-host implications; otherwise restructure to avoid the redirect
POST body is dropped on 301/302 Add --post301 --post302 (or use 307/308 redirects on the server)
curl: (60) SSL certificate problem Fix the chain or use --cacert /path/to/ca.pem — never -k in production
Stuck waiting for slow upstream Always set both --connect-timeout and --max-time
JSON body fails with double-quote escaping Use --json (curl 7.82+) or --data @file.json
curl: (6) Could not resolve host in CI DNS-over-TCP at runner start — add --retry 3 --retry-all-errors to absorb the cold start
Output garbled in CI logs -sS (silent, but show errors) instead of -s
Progress bar in script output -s or --no-progress-meter
curl --json not recognized Pre-7.82 curl — use -H "Content-Type: application/json" -d @body.json

Companion: full DeployHQ deploy pipeline

curl is the verb that ties the build phase to the deploy phase. The full pattern — CI emits a webhook, DeployHQ orchestrates the release, post-deploy curl smoke tests gate the rollout — is documented in the AI-agent-driven CI/CD walkthrough. For projects where the deploy lives entirely behind a webhook trigger, see DeployHQ integrations.

Start a free DeployHQ trial to wire the webhook in five minutes.

Need help? Email support@deployhq.com or follow @deployhq on X.