Chapter 13Lesson 01~95 minutes

curl Fundamentals for HTTP Automation

`curl` turns HTTP into a scriptable interface, but reliable automation requires more than fetching a URL. Production scripts need timeouts, status classification, safe credential handling, and clear separation between transport errors, HTTP responses, and response bodies.

IntermediateAPIs & structured dataHands-on lab

Learning objectives

By the end of this lesson

  • Make bounded HTTP requests with curl.
  • Separate transport status from HTTP status.
  • Send headers and JSON safely.
  • Capture bodies without corrupting stdout contracts.
  • Stage downloads before promotion.

1. curl turns HTTP into a shell command

curl is one of the most useful tools for DevOps automation because it can make HTTP requests, send headers and bodies, follow redirects, enforce timeouts, and expose transport failures through exit status.

HTTP request flow
flowchart LR
  B["Bash"] --> C["curl"]
  C --> H["HTTP server"]
  H --> R["status + headers + body"]
  R --> C
  C --> B

2. Start with a simple GET request

curl https://api.example.com/health

By default, the response body is written to stdout. Production scripts usually add explicit failure, timeout, and output options.

3. Make HTTP errors visible to shell control flow

if curl --fail --silent --show-error "$url"; then
  printf 'request succeeded\n' >&2
else
  status=$?
  printf 'curl failed status=%d\n' "$status" >&2
  exit "$status"
fi

--fail makes selected HTTP error responses produce a non-zero exit status instead of being treated as transport success.

HTTP versus transport

A request can fail before HTTP exists, or receive an HTTP response that indicates application failure. Your script should understand both layers.

4. Bound connection and total request time

curl \
  --connect-timeout 5 \
  --max-time 30 \
  --fail \
  --silent \
  --show-error \
  "$url"

A connect timeout limits connection establishment; a total timeout bounds the entire transfer.

5. Send request headers explicitly

curl \
  --fail \
  --silent \
  --show-error \
  --header 'Accept: application/json' \
  --header "Authorization: Bearer $API_TOKEN" \
  "$url"
Secret handling

Do not print the complete command line when it contains credentials. Debug output, process inspection, shell history, and CI logs can all expose tokens.

6. Use the HTTP method that matches the API contract

MethodTypical roleAutomation concern
GETRead a resourceNormally no request body
POSTCreate/actionOften non-idempotent
PUTReplace/update desired stateOften idempotent by contract
PATCHPartial updateSemantics depend on API
DELETEDelete resourceMay be idempotent depending on API
curl --request DELETE \
  --fail --silent --show-error \
  "$url"

7. Send JSON without accidental shell interpolation

payload=$(jq -n \
  --arg service "$service" \
  --arg environment "$environment" \
  '{service: $service, environment: $environment}')

curl \
  --request POST \
  --header 'Content-Type: application/json' \
  --data "$payload" \
  --fail --silent --show-error \
  "$url"

Building JSON with jq is safer than manually concatenating quotes and escaped characters in Bash.

8. Capture response body and HTTP status separately

tmp=$(mktemp) || exit 1

http_code=$(
  curl \
    --silent \
    --show-error \
    --output "$tmp" \
    --write-out '%{http_code}' \
    "$url"
)
curl_status=$?

if (( curl_status != 0 )); then
  printf 'transport failure=%d\n' "$curl_status" >&2
  rm -f -- "$tmp"
  exit "$curl_status"
fi

printf 'http_code=%s\n' "$http_code" >&2
cat "$tmp"
rm -f -- "$tmp"

This pattern separates transport status, HTTP status, and body content instead of flattening them into one string.

9. Redirect following should be intentional

curl --location --fail --silent --show-error "$url"

--location follows redirects. Review security and credential-forwarding implications when requests cross hosts or trust boundaries.

10. Download to a file with explicit failure behavior

tmp=$(mktemp) || exit 1

if curl \
  --fail \
  --silent \
  --show-error \
  --output "$tmp" \
  "$artifact_url"; then
  mv -- "$tmp" artifact.tar.gz
else
  status=$?
  rm -f -- "$tmp"
  exit "$status"
fi

Staging the download keeps a partial transfer from appearing under the final artifact name.

11. Hands-on lab: build an HTTP helper

mkdir -p "$HOME/devops-academy/bash/chapter13/lesson01"
cd "$HOME/devops-academy/bash/chapter13/lesson01"

cat > http-get.sh <<'EOF'
#!/usr/bin/env bash
set -u
set -o pipefail

url=${1:-}
[[ -n $url ]] || {
  printf 'usage: %s URL\n' "$0" >&2
  exit 64
}

command -v curl >/dev/null 2>&1 || {
  printf 'curl is required\n' >&2
  exit 69
}

tmp=$(mktemp) || exit 1
trap 'rm -f -- "$tmp"' EXIT

if http_code=$(
  curl \
    --connect-timeout 5 \
    --max-time 20 \
    --silent \
    --show-error \
    --output "$tmp" \
    --write-out '%{http_code}' \
    "$url"
); then
  printf 'http_code=%s\n' "$http_code" >&2
  cat "$tmp"
else
  status=$?
  printf 'transport failure=%d\n' "$status" >&2
  exit "$status"
fi
EOF

chmod u+x http-get.sh
./http-get.sh https://example.com >/dev/null

Verification checklist

12. Knowledge check

Question 1. What does --fail change?

Question 2. Why separate HTTP status from curl exit status?

Question 3. Why build JSON with jq instead of string concatenation?

Question 4. Why download to a temporary file first?

13. Summary

Reliable curl automation uses explicit timeouts, transport-status checks, HTTP-status handling, safe body construction, protected credentials, and staged file writes. Treat HTTP as a protocol with several independent failure layers.

14. Further reading

  • curl documentation — exit codes, timeouts, headers, redirects, output formatting.
  • HTTP semantics — methods and status codes.
  • jq manual — constructing JSON values.
  • OWASP guidance — secrets in logs and command execution.
Next lesson

Parsing JSON Safely with jq

Continue Chapter 13 by making structured API and data handling safer and more predictable.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.