🌐 curl Quick Reference

cheat sheet / survival guide

Video: curl (Command Line Tips video)
Prefer to watch? This short video runs this page's main commands on a real Ubuntu 24.04 machine, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
curl [options] URL
curl -sS https://example.com

curl transfers data to and from servers using a URL. It speaks HTTP and HTTPS (and many other protocols: FTP, SFTP, SMTP, IMAP, LDAP, and more). Sysadmins and developers use it to test websites
and APIs, download files, check certificates, time requests, send JSON, upload files, and script health checks. By default curl prints the response body to the terminal and stays quiet about
everything else. This sheet covers the flags you will use every day, plus recipes for APIs, downloads, authentication, TLS, debugging and scripting.
Check your version:  curl --version   (features like --json, --fail-with-body and HTTP/3 need newer versions: noted below).  Alternatives: wget (downloads), HTTPie (friendlier API client).

The basics

curl https://example.com                         # GET a page and print the body
curl -s https://example.com                      # -s silent: hide the progress meter and errors (good for scripts and pipes)
curl -sS https://example.com                     # -sS silent but still show errors (usually the best pair for scripts)
curl -i https://example.com                      # -i include the response HEADERS before the body
curl -I https://example.com                      # -I HEAD request: headers only, no body (a quick "is it up?" check)
curl -v https://example.com                      # -v verbose: shows the connection, TLS handshake, request and response headers (lines start with > sent, < received, * info)
curl -o page.html https://example.com            # -o save to a file you name          curl -O https://example.com/file.zip    -O save using the remote file name
curl -L http://example.com                       # -L follow redirects (301, 302, 307, 308). Without it you get the redirect page
curl -f https://example.com/missing              # -f fail silently on HTTP errors (4xx/5xx): exit code 22 and no body
curl --fail-with-body https://example.com/api    # like -f but still prints the error body (curl 7.76+): great for APIs
curl -sSf -o /dev/null https://example.com && echo up   # the classic health check: no output, exit code tells the result
curl "https://example.com/search?q=hello world"   # quote URLs that contain ? & spaces or the shell will misread them (& sends the command to the background)
curl -G --data-urlencode "q=hello world" https://example.com/search        # build a query string safely: -G turns data into GET parameters
Output: HTTP status and headers are only shown with -i, -I or -v.  Errors go to stderr, the body to stdout, so  curl url > out.txt  saves just the body.

Request methods and sending data

curl -X GET https://api.example.com/items        # -X sets the method (GET is the default: you rarely need -X GET)
curl -X POST https://api.example.com/items -d "name=widget&qty=3"          # -d sends form-style data (and switches to POST automatically, so -X POST is optional)
curl -X PUT  https://api.example.com/items/7 -d '{"qty":5}' -H "Content-Type: application/json"
curl -X PATCH  https://api.example.com/items/7 -d '{"qty":6}' -H "Content-Type: application/json"
curl -X DELETE https://api.example.com/items/7
curl -X OPTIONS -i https://api.example.com/items                            # see which methods and CORS headers the server allows
curl -d @data.json https://api.example.com/items -H "Content-Type: application/json"       # @file reads the body from a file
curl -d @- https://api.example.com/items -H "Content-Type: application/json" < data.json    # @- reads from standard input (also: echo '{"a":1}' | curl -d @- ...)
curl --data-urlencode "comment=hello & goodbye" https://example.com/form                     # URL-encodes the value for you
curl --json '{"name":"widget","qty":3}' https://api.example.com/items                        # curl 7.82+: sets Content-Type and Accept to application/json for you, and POSTs
curl --json @data.json https://api.example.com/items
curl -F "file=@photo.jpg" -F "title=Holiday" https://example.com/upload                      # -F multipart form upload (like an HTML form with a file field)
curl -F "file=@photo.jpg;type=image/jpeg;filename=pic.jpg" https://example.com/upload        # set the MIME type and file name
curl -T backup.tar.gz https://example.com/uploads/backup.tar.gz                              # -T uploads with PUT (WebDAV, S3-style URLs, FTP)
curl --data-binary @image.png -H "Content-Type: image/png" https://example.com/api/upload      # send raw bytes exactly as they are (-d strips newlines)
-d sends application/x-www-form-urlencoded by default. For JSON always add  -H "Content-Type: application/json"  (or use --json).
Single quotes around JSON avoid shell trouble:  -d '{"name":"a b"}' .  For variables use double quotes and escape:  -d "{\"name\":\"$NAME\"}"  or build it with jq (see below).

Headers, cookies and identity

curl -H "Accept: application/json" https://api.example.com/items              # -H adds or replaces a header (use it more than once for several)
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me             # a bearer token (API keys and OAuth)
curl -H "Host: example.com" http://203.0.113.10/                             # test a specific server IP with a virtual host name (skips DNS)
curl -H "X-Forwarded-For: 1.2.3.4" ...                                       # custom headers
curl -H "Accept-Encoding:" ...                                               # a header with nothing after the colon REMOVES it        curl -H "Expect:" ...   turn off Expect: 100-continue
curl -H @headers.txt https://api.example.com/                                # read headers from a file (curl 7.55+): keeps secrets out of history and process lists
curl -A "Mozilla/5.0" https://example.com                                    # -A user agent (--user-agent)          curl -e https://google.com https://example.com    -e referer
curl -b "session=abc123" https://example.com/                                # -b send a cookie          curl -b cookies.txt https://example.com    send cookies from a file
curl -c cookies.txt -b cookies.txt -L https://example.com/login -d "user=a&pass=b"      # -c save cookies the server sets. Use both to keep a session across several requests
curl --compressed https://example.com                                        # ask for gzip/brotli and decompress the answer automatically
curl -r 0-1023 https://example.com/big.iso -o first-kb.bin                    # -r range: fetch only part of a file (if the server supports it)

Authentication

curl -u alice:secret https://example.com/private                             # -u basic authentication (the password is visible in your history and the process list!)
curl -u alice https://example.com/private                                    # -u USER only: curl prompts for the password (safer)
curl --netrc https://example.com/                                            # read credentials from ~/.netrc (chmod 600):   machine example.com login alice password secret
curl -H "Authorization: Bearer $(cat ~/.token)" ...                          # a token from a protected file
curl --oauth2-bearer "$TOKEN" https://mail.example.com/                      # also for IMAP/SMTP
curl --digest -u alice:secret https://example.com/                           # digest auth          --ntlm -u ...    NTLM         --negotiate -u : https://intranet/    Kerberos (after kinit)
curl --cert client.pem --key client.key https://api.example.com/             # a client certificate (mutual TLS)         --cert client.p12:password    PKCS#12
curl -K - https://example.com/ <<< 'header = "Authorization: Bearer SECRET"'  # -K reads options from a file or stdin: hides the secret from the process list and history
Never put real passwords or tokens directly on the command line on a shared machine: other users can see them with ps, and your shell saves them in its history. Use prompts, ~/.netrc, -K, or -H @file.

Working with JSON APIs (with jq)

curl -s https://api.github.com/repos/curl/curl | jq '.stargazers_count'                     # pull one value out of the answer
curl -s https://api.example.com/items | jq -r '.[] | "\(.id)\t\(.name)"'                    # a table from a JSON array
curl -s https://api.example.com/items | jq .                                                 # pretty-print JSON
jq -n --arg n "$NAME" '{name:$n}' | curl -s -X POST https://api.example.com/items -H "Content-Type: application/json" -d @-           # build JSON safely from a variable, no quoting trouble
curl -s -X POST https://api.example.com/login --json '{"user":"a","pass":"b"}' | jq -r .token      # log in and capture a token
TOKEN=$(curl -s ... | jq -r .token) ; curl -H "Authorization: Bearer $TOKEN" https://api.example.com/me
Pretty-print without jq:  curl -s URL | python3 -m json.tool

Downloads

curl -O https://example.com/file.iso                       # save as file.iso in the current folder
curl -o ubuntu.iso https://example.com/file.iso            # save under your own name
curl -OL https://example.com/latest                        # follow redirects and use the final file name       curl -JLO URL    -J use the name the server suggests (Content-Disposition)
curl -C - -O https://example.com/big.iso                   # -C - RESUME an interrupted download
curl --limit-rate 500k -O https://example.com/big.iso      # cap the speed (k, m)
curl -O https://example.com/file1.zip -O https://example.com/file2.zip        # several files in one command
curl -O "https://example.com/img[1-20].jpg"                # URL globbing: img1.jpg to img20.jpg       "img[01-20].jpg" keeps leading zeros      "https://{a,b,c}.example.com/"   a list
curl --create-dirs -o "downloads/$(date +%F)/report.pdf" https://example.com/report.pdf      # make missing directories
curl --retry 5 --retry-delay 2 --retry-connrefused -O https://example.com/file.iso           # retry transient failures       --retry-all-errors    retry on any error (7.71+)
curl -Z -O "https://example.com/img[1-100].jpg"            # -Z / --parallel: download several at once (7.66+)         --parallel-max 10    limit
curl -sS -o /dev/null -w '%{size_download} bytes in %{time_total}s\n' https://example.com/big.iso     # measure a download without saving it
curl -s https://example.com/file.sh | sh                   # DANGEROUS: piping a download into a shell runs unreviewed code. Download, read it, then run it
Verify what you download: compare a checksum from the publisher (sha256sum file) or check a signature.

Timing, status codes and measuring (-w write-out)

curl -s -o /dev/null -w '%{http_code}\n' https://example.com                   # just the HTTP status code (200, 301, 404...)
curl -s -o /dev/null -w '%{http_code} %{time_total}s %{size_download}B\n' https://example.com
curl -sS -o /dev/null -w 'dns: %{time_namelookup}  connect: %{time_connect}  tls: %{time_appconnect}  first byte: %{time_starttransfer}  total: %{time_total}\n' https://example.com
curl -s -o /dev/null -w '%{url_effective} %{redirect_url}\n' -L https://example.com/short            # where did a redirect end up?
curl -s -o /dev/null -w '%{remote_ip}:%{remote_port} %{http_version} %{ssl_verify_result}\n' https://example.com
curl -s -o /dev/null -w '%{json}' https://example.com | jq .                    # everything as JSON (7.70+)
Useful write-out variables:  http_code  time_namelookup  time_connect  time_appconnect (TLS done)  time_pretransfer  time_starttransfer (time to first byte)  time_total  size_download  size_upload  speed_download
                             num_redirects  url_effective  redirect_url  remote_ip  local_ip  http_version  content_type  ssl_verify_result  exitcode  errormsg
Save the format in a file  curl-format.txt  and reuse it:   curl -s -o /dev/null -w @curl-format.txt https://example.com

TLS, HTTPS and certificates

curl https://example.com                                   # verifies the server certificate by default. If it fails you see  curl: (60) SSL certificate problem
curl -k https://self-signed.example.com                    # -k / --insecure: SKIP certificate checks. Fine for a lab, dangerous anywhere else (you lose protection against impostors)
curl --cacert ca.crt https://internal.example.com          # trust your own CA (or a self-signed cert)         --capath /etc/ssl/certs    a directory of CAs
curl -v https://example.com 2>&1 | grep -E "subject|issuer|expire|SSL connection|TLS"                # certificate and protocol info
curl --tlsv1.2 --tls-max 1.2 https://example.com           # force a TLS version (--tlsv1.3, --tls-max 1.3)        --ciphers ECDHE-RSA-AES128-GCM-SHA256    choose TLS 1.2 ciphers
curl --resolve example.com:443:203.0.113.10 https://example.com/            # pretend example.com is at that IP (test a new server or a CDN origin before changing DNS, with correct TLS names)
curl --connect-to example.com:443:backend.internal:443 https://example.com/   # connect to a different host but keep the original name for TLS and Host
curl --cert-status https://example.com                     # require a valid OCSP stapled response
curl --pinnedpubkey "sha256//BASE64HASH=" https://example.com    # public key pinning
curl -sI https://example.com | grep -i -E "strict-transport|server|content-type"           # security and server headers
Get the certificate chain:  echo | openssl s_client -connect example.com:443 -servername example.com -showcerts   (see the OpenSSL sheet)

Protocol versions and HTTP details

curl --http1.1 https://example.com          curl --http2 https://example.com          curl --http2-prior-knowledge http://localhost:8080          curl --http3 https://example.com  (needs an HTTP/3 build)
curl -sI --http2 https://example.com | head -1        # "HTTP/2 200" confirms HTTP/2 was used            curl -w '%{http_version}\n' -o /dev/null -s https://example.com
curl -4 https://example.com     curl -6 https://example.com                 # force IPv4 or IPv6 (great for testing dual-stack problems)
curl --interface eth1 https://example.com                                   # send from a specific network interface or IP
curl --local-port 40000 https://example.com
curl -H "Connection: close" ...     curl --keepalive-time 30 ...
curl -w '%{num_connects}\n' ... (how many connections were made)

Proxies

curl -x http://proxy.example.com:3128 https://example.com                   # -x use a proxy         (also: export https_proxy=http://proxy:3128  and  export no_proxy=localhost,.internal)
curl -x socks5h://127.0.0.1:1080 https://example.com                        # SOCKS5 with proxy-side DNS (the h) — great with  ssh -D 1080 server  for a quick tunnel
curl -U proxyuser:proxypass -x http://proxy:3128 https://example.com        # proxy authentication
curl --noproxy '*' https://example.com                                      # ignore proxy settings for this call
curl -p -x http://proxy:3128 https://example.com                            # tunnel through the proxy with CONNECT

Timeouts, retries and reliability (essential in scripts)

curl --connect-timeout 5 https://example.com               # give up connecting after 5 seconds
curl -m 20 https://example.com                              # -m / --max-time: the whole operation must finish within 20 seconds
curl --speed-limit 1000 --speed-time 15 -O URL              # abort if slower than 1000 bytes/s for 15 seconds
curl --retry 3 --retry-delay 2 --retry-max-time 60 https://example.com      # retry on timeouts and 5xx (and 429)
curl -sS --fail-with-body --connect-timeout 5 -m 30 --retry 3 https://api.example.com/health      # a solid script-friendly baseline
Without timeouts a script can hang forever on a dead server. Always set --connect-timeout and -m in cron jobs and monitoring.

Other protocols

curl ftp://ftp.example.com/file.txt -u user:pass -O                          # FTP download          curl -T local.txt ftp://ftp.example.com/dir/ -u user:pass    upload         curl --ssl-reqd ftp://...    require TLS (FTPS)
curl sftp://server.example.com/home/alice/file.txt -u alice: --key ~/.ssh/id_ed25519 -O        # SFTP with a key (curl must be built with libssh2)
curl scp://server/path/file -u alice: --key ~/.ssh/id_ed25519 -O
curl --url "smtps://smtp.example.com:465" --mail-from me@example.com --mail-rcpt you@example.com --user 'me@example.com:PASSWORD' -T mail.txt        # send email (mail.txt holds headers, a blank line, and the body)
curl "imaps://imap.example.com/INBOX" --user 'me@example.com:PASSWORD' -X 'SEARCH UNSEEN'            # read mail over IMAP
curl "ldap://ldap.example.com/dc=example,dc=com?cn,mail?sub?(uid=alice)"                              # LDAP query
curl telnet://host:23 ;   curl -v telnet://host:25    or   curl -v --connect-timeout 5 telnet://host:port          # test whether a TCP port is open (curl prints "Connected")
curl dict://dict.org/d:curl ;  curl gopher://... ; curl file:///etc/hostname     # more protocols (curl -V shows the ones your build supports)
curl ws://echo.example.com ; curl wss://... (WebSocket support is new and experimental in recent curl versions)

Debugging: see what is really happening

curl -v https://example.com                                # headers and TLS in both directions      * = info, > = sent, < = received
curl -vvv ...                                              # not needed: -v is already verbose        curl --trace-ascii trace.txt URL    a full ASCII dump of all data sent and received (great for support tickets: REDACT secrets)
curl --trace-time --trace-ascii - URL                      # with timestamps, to the screen
curl -D headers.txt -o body.txt https://example.com        # -D save the response headers separately from the body
curl -s -D - -o /dev/null https://example.com              # print only the headers (to stdout) and discard the body
curl --path-as-is "https://example.com/a/../b"             # do not normalize the URL path (test path handling)
curl -w '\n%{http_code}\n' ...                             # add the status after the body so you can see both
curl --stderr - -sS URL                                    # send curl's own messages to stdout
curl -sS https://httpbin.org/anything -d 'a=1'             # an echo service that shows exactly what you sent (a great learning tool)
When something fails:  add  -v  first. Then read the FIRST error line: DNS (Could not resolve host), connect (Connection refused, timed out), TLS (SSL certificate problem, handshake failure), or HTTP (the status code).

Exit codes (for scripts: echo $? after curl)
0   Success                                       22  HTTP error with -f (the server answered 4xx or 5xx)              3   Malformed URL (check quotes and special characters)
6   Could not resolve host (DNS)                  7   Failed to connect (refused, or nothing listening)                 28  Operation timed out (--connect-timeout or -m)
35  TLS handshake failed                          51/60  Certificate does not verify (60 = untrusted CA or wrong name)  52  Empty reply from the server (it closed without answering)
56  Failure receiving data (connection reset)     18  Partial transfer (connection dropped)                            23  Write error (disk full, or a broken pipe)
47  Too many redirects (raise --max-redirs, default 50)     67 Login denied          92 HTTP/2 stream error
Full list:  man curl  (search EXIT CODES)   or   curl --help
Scripts:   code=$(curl -s -o body.txt -w '%{http_code}' URL) ; [ "$code" = 200 ] || { echo "failed: $code"; exit 1; }

Config files, aliases and defaults

~/.curlrc                        # options applied to EVERY curl call (one per line, without the leading dashes if you like). Example:
                                 #     silent
                                 #     show-error
                                 #     connect-timeout = 10
                                 #     user-agent = "my-curl/1.0"
curl -q ...                      # -q (first option!) ignores ~/.curlrc          curl -K request.conf    read options from a file for one request (great to keep secrets out of history)
Example  request.conf:   url = "https://api.example.com/items"  \n  header = "Authorization: Bearer XXXXX"  \n  header = "Accept: application/json"   (chmod 600 the file)
alias curlt='curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n"'      # a quick status-and-time helper
export CURL_CA_BUNDLE=/path/ca.crt     # (used by some builds) and  SSL_CERT_FILE=/path/ca.crt    a custom CA bundle
Environment: http_proxy, https_proxy, no_proxy (proxy settings) and CURL_HOME. curl deliberately ignores the UPPERCASE HTTP_PROXY variable (a security precaution for CGI programs): use lowercase http_proxy. The other proxy variables accept either case.

Recipes

# Is a site up? (status code, time, redirects)
curl -sSL -o /dev/null -w '%{http_code} %{time_total}s (%{num_redirects} redirects) %{url_effective}\n' https://example.com

# Health check for monitoring: exit non-zero on any problem
curl -sSf --connect-timeout 5 -m 10 -o /dev/null https://example.com/health || echo "DOWN"

# Compare the same URL on two servers before a cutover
curl -s --resolve www.example.com:443:203.0.113.10 https://www.example.com/ | md5sum ; curl -s --resolve www.example.com:443:203.0.113.20 https://www.example.com/ | md5sum

# Time each phase of a slow request
curl -so /dev/null -w 'dns %{time_namelookup} | tcp %{time_connect} | tls %{time_appconnect} | ttfb %{time_starttransfer} | total %{time_total}\n' https://example.com

# What are my public IP addresses? (IPv4 and IPv6)
curl -4 -s https://ifconfig.me ; echo ; curl -6 -s https://ifconfig.me

# Follow a redirect chain and show each hop's status and location
curl -sIL https://example.com | grep -iE "^(HTTP|location)"

# Test HTTP to HTTPS redirect and HSTS
curl -sI http://example.com | grep -iE "^(HTTP|location)" ; curl -sI https://example.com | grep -i strict-transport

# Post JSON with a token, and fail loudly
curl -sS --fail-with-body -H "Authorization: Bearer $TOKEN" --json '{"status":"done"}' https://api.example.com/jobs/7

# Upload a file to an API and print the result nicely
curl -sS -F "file=@report.pdf" -H "Authorization: Bearer $TOKEN" https://api.example.com/upload | jq .

# Download the newest release asset from GitHub (public repo)
curl -sL https://api.github.com/repos/OWNER/REPO/releases/latest | jq -r '.assets[].browser_download_url' | head -1 | xargs curl -LO

# Check if a TCP port is reachable from this machine (like nc -z)
curl -v --connect-timeout 3 telnet://db.example.com:5432 2>&1 | grep -E "Connected|refused|timed out"

# Download many URLs listed in a file, 4 at a time
xargs -n1 -P4 curl -sSfLO < urls.txt

# Send a Slack-style webhook message
curl -sS -X POST -H 'Content-type: application/json' --data '{"text":"Deploy finished"}' "$WEBHOOK_URL"

# Retry until a service answers (wait for startup)
until curl -sf http://localhost:8080/health >/dev/null; do sleep 2; done; echo "ready"

# Test with a different Host header on the local server
curl -sI -H "Host: www.example.com" http://127.0.0.1/ | head -1

# Check that gzip is on
curl -sI -H "Accept-Encoding: gzip" https://example.com | grep -i content-encoding

# Fetch just one line of the output, quietly
curl -s https://example.com | grep -o '<title>.*</title>'

Common problems

"curl: (6) Could not resolve host"                     DNS failure or a typo. Try  dig host ; nslookup host . On a network with a proxy set http_proxy/https_proxy
"curl: (7) Failed to connect ... Connection refused"    Nothing is listening on that port, or the wrong port. Check the service and firewall (ss -tlnp, nc -zv host port)
"curl: (28) Connection timed out" / "Operation timed out"   A firewall drops packets, the host is down, or it is too slow. Adjust --connect-timeout and -m, and test from another network
"curl: (35) ... SSL connect error" / "wrong version number"   Protocol mismatch: you used https:// on a plain HTTP port (or the reverse), or old TLS versions. Check the port and scheme
"curl: (60) SSL certificate problem: unable to get local issuer certificate"    The CA is not trusted or the server sends an incomplete chain. Use --cacert, install the CA, or fix the server's chain. Use -k only for a quick lab test
"curl: (60) ... certificate has expired" / "no alternative certificate subject name matches"   Expired certificate, or the name you used is not in the certificate (use the right host name, or --resolve with the real name)
"curl: (52) Empty reply from server"                   The server closed the connection without answering: a crash, a WAF block, or you sent HTTP to a TLS port
"curl: (56) Recv failure: Connection reset by peer"   A firewall or proxy reset it, or the server crashed. Try  -v , another network, or --http1.1
"curl: (22) The requested URL returned error: 403/404"   The server said no. Add -i or --fail-with-body to read the answer. Check the URL, method, headers and authentication
"curl: (3) URL using bad/illegal format or missing URL"   Unquoted special characters or spaces in the URL: quote it. For [] and {} use  -g  (--globoff)
The shell runs my command in the background (an & in the URL)   Quote the URL: curl "https://example.com/?a=1&b=2"
Data sent with -d arrives as form data, not JSON       Add  -H "Content-Type: application/json"  or use --json
-d "@file" strips newlines from the file               Use --data-binary @file
POST turns into GET after a redirect                   -L follows 301/302 by changing POST to GET. Use --post301 --post302 or the 307/308 redirect codes if the API needs POST preserved
Output is garbled binary                               The server sent gzip or a binary file. Use --compressed, or save with -o file
Progress meter clutters a script's output              Use -s (and -S to keep error messages), or --no-progress-meter
"Warning: Binary output can mess up your terminal"     You are printing a binary file to the terminal. Use -o file, or -O
Works in the browser but not with curl                 Cookies, a required User-Agent or Referer header, JavaScript-generated content, or bot protection. Compare with -v and the browser's DevTools ("Copy as cURL" gives the exact command)
Password with special characters fails                 Quote it in single quotes, or use -u USER and type the password at the prompt

Tips

    Use -sS for quiet scripts that still show errors, and add --fail-with-body (or -f) so HTTP errors make curl exit with a non-zero code.

    Always set --connect-timeout and -m in scripts and cron jobs. A hung request can block a job forever.

    Use -v first when something is wrong. Read the first error, then work out whether it is DNS, connect, TLS or HTTP.

    Quote every URL that has ? or &, and use single quotes around JSON. Use jq -n to build JSON from variables, so quoting bugs cannot bite.

    Keep secrets out of your history and the process list: use prompts, ~/.netrc, -K, or -H @file. Never paste tokens into tickets, and redact traces.

    Do not use -k in real systems. Fix the certificate or trust the right CA (--cacert) instead. Use --resolve to test servers before changing DNS.

    Use -w '%{http_code} %{time_total}' with -o /dev/null for quick status and timing checks, and the phase timings (dns, connect, tls, ttfb) to find where slowness lives.

    Never pipe curl into a shell (curl ... | sh) unless you trust the source, and prefer downloading, reading and then running.

    Browser DevTools can "Copy as cURL": paste it to reproduce a request exactly, then remove the cookies and tokens before sharing.

    Feature flags depend on your version: --json needs 7.82+, --fail-with-body 7.76+, --parallel 7.66+, HTTP/3 needs a special build. Check curl --version.