Learn to see through yourself: -v and -i reveal a whole request
The beginning of debugging is not guessing but seeing exactly what you sent and received. curl -v prints the request line, request headers, TLS handshake, and response headers in full; -i shows the response headers rather than only the status code. Together they are the watershed between "the API rejected me" and "my request is malformed".
Watch a few fields closely: whether Content-Type matches the body, whether Authorization rides under the right header name, and whether Host matches the address. This one-liner shows both directions at once.
curl -v https://api.example.com/v1/users -H "Authorization: Bearer $TOKEN"
# 只看响应头
curl -sI https://api.example.com/v1/users
# 若要 -v 的详实又要输出干净,可重定向 body
curl -v -o /dev/null https://api.example.com/v1/usersMethod, query, and body: no data in GET, right Content-Type for POST
Under REST semantics, GET only retrieves and carries nothing of substance (filters belong in the query string after ?); POST puts its payload in the body. Two classic problems follow: hard-joining query filters onto a POST path so routing fails, or POSTing JSON without declaring application/json so the server parses a pile of undefined out of a form body.
In curl, a JSON body sets Content-Type with -H and sends a string with -d; --data-urlencode handles forms/query with special characters. Below are the three in contrast: GET query, POST+JSON, POST+form.
GET
curl -sG https://api.example.com/v1/users --data-urlencode "page=1" --data-urlencode "name=张"
POST JSON
curl -s -X POST https://api.example.com/v1/users \
-H "Content-Type: application/json" \
-d '{"name":"alice","active":true}'
POST form
curl -s -X POST https://api.example.com/v1/login \
-d "username=alice&password=secret"Uploads and files: the right multipart/form-data shape with progress
File uploads use multipart/form-data; curl specifies the field name and local path with -F and generates the boundary and Content-Type itself, so hand-writing a boundary is always unnecessary trouble.
Flatten several -F for a multi-field, multi-file request; for big files use --progress-bar to watch progress, and --limit-rate to keep an upload from saturating the link. One more gotcha: quote paths containing spaces.
单文件
curl -X POST https://api.example.com/files \
-H "Authorization: Bearer $TOKEN" \
-F "file=@/tmp/report.pdf" -F "note=for review"
多文件带进度与限速
curl --progress-bar --limit-rate 5M \
-F "a=@a.zip" -F "b=@b.zip" \
-H "Authorization: Bearer $TOKEN" \
https://api.example.com/archive
# 只上传字节流而非文件(用分号或 < 重定向)
cat body.json | curl -d @- https://api.example.com/parseDiagnose responses: status code, timing, and redirects in one read
When "the request went out but the result is wrong", read the response itself: -w hands you http_code and multi-segment timing (DNS, TCP, TLS, starttransfer, total) in one shot. Slow-DNS vs slow-handshake vs slow-body transfer each demand very different countermeasures.
Automatically follow redirects with -L, or a 301/302 returns just an empty jump page. Combining -w with -o /dev/null is the standard move to align real backend latency with the vague "the front feels slow". Below is a full timing probe.
curl -s -o /dev/null -w "http=%{http_code} \nDNS=%{time_namelookup}s TCP=%{time_connect}s TLS=%{time_appconnect}s TTFB=%{time_starttransfer}s total=%{time_total}s \nredirs=%{num_redirects} url=%{url_effective}\n" \
-L https://api.example.com/v1/orders
# 只看重定向链 不拉 body
curl -sIL https://api.example.comMistake vs fix: stop guessing at certificate and encoding errors
Self-signed or internal https servers raise SSL certificate problem: self-signed; the correct remedy is importing the CA locally or adding --cacert—flinging -k as a brute-force bypass is a discouraged backstop that also opens the door to MITM.
Another frequent offender is garbage output, usually the server returns gzip while you dump it raw to text: use --compressed so curl decompresses, otherwise you debug mojibake and never find the root cause. Convert both cases from guessing to the right flag.
# 错误示范:关闭校验证书绕坑
# curl -k https://internal.example /api
# 修复对照:用自家 CA 或临时 cacert
curl --cacert /etc/ssl/certs/ca.pem https://internal.example/api
# 错误示范:不解压存乱码
# curl -s https://api.example/export > out.txt
# 修复:--compressed 自动解压
curl --compressed -s https://api.example/export > out.txt