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.
Related cheatsheets
- Bash scripting cheatsheet — for the script harness that wraps every curl call (
set -euo pipefail, lockfiles, exit codes). - SSH cheatsheet — for the deploy-host side of the same pipeline.
- jq cheatsheet — for parsing the JSON your curl calls return.
- Claude Code cheatsheet — for asking an AI agent to draft, debug, and audit these curl-based deploy hooks.
- Cheatsheets hub — every DeployHQ cheatsheet in one place.
Need help? Email support@deployhq.com or follow @deployhq on X.