调试一个 HTTP 接口时,浏览器开发者工具、Postman 之类的图形工具都要先绕一层——装客户端、登录、配代理。curl 不用。它是 Ubuntu 24.04 自带的命令行 HTTP 客户端,装完系统第一分钟就能用,SSH 连上一台陌生服务器,它往往是你唯一可用的调试工具。
Ubuntu 24.04 仓库里的 curl 是 8.5.0(安全补丁版 8.5.0-2ubuntu10.8,apt-cache policy curl 可查),下文所有命令和参数都在这个版本验证过。低版本如 7.81(Ubuntu 22.04)的参数绝大多数也适用,个别如 --retry-all-errors 需要 curl 7.71+,遇到参数不认识先 curl --version 看一眼。
环境差异是最常见的翻车点,不是参数记错。 服务器上 curl 版本老、本地版本新,同一个命令行为不同,先确认两边版本再排查。
先建立坐标系:URL 与请求方法
curl 的第一个参数就是 URL,方法、头、体都靠选项附加。最基本的一条命令只有 URL:
curl https://httpbin.org/get
默认方法是 GET,响应体直接打到终端。选项的记法有规律:单字母短选项(-o、-d、-I)是高频操作,长选项(—resolve、—connect-timeout)是低频但功能重的操作。 两套可以混用,curl -d '{}' https://example.com/api 和 curl --data '{}' https://example.com/api 完全等价。
调试时先记住两件事:方法用 -X 显式指定(如 -X POST),头用 -H 追加(可重复多次)。这两个选项贯穿后面所有场景。
场景 1-5:请求体怎么发
场景 1:GET,只想快速看一眼响应体。 最简形式,URL 直接跟:
curl https://httpbin.org/get
场景 2:POST 表单。 表单字段用 -d,curl 自动加 Content-Type: application/x-www-form-urlencoded:
curl -d "username=admin&password=secret" https://httpbin.org/post
场景 3:POST JSON。 显式声明 Content-Type,body 用单引号包住,shell 不会拆词:
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"nginx","version":"1.24"}' \
https://httpbin.org/post
# 从文件读 body:
curl -d @payload.json https://httpbin.org/post
-d @文件名 是调大请求体时的标准做法,命令行放不下长 JSON。单引号包 JSON 是 curl 调试的第一条纪律——用双引号,shell 会把 $ 和 ` 都展开,body 就变了样。
场景 4:PUT / DELETE / PATCH。 方法换了,结构不变:
curl -X PUT -d '{"status":"on"}' https://httpbin.org/put
curl -X DELETE https://httpbin.org/delete
场景 5:只想看响应头,不要 body。 -I(大写 i)发 HEAD 请求,只返回头:
curl -I https://example.com
适合快速确认状态码、缓存头、重定向目标。注意 -I 发的是 HEAD 请求——有些接口对 HEAD 返回和 GET 不同的状态码,要看 GET 的真实响应头用 -s -o /dev/null -D -,这个组合在场景 10 展开。
场景 6-10:响应怎么读
场景 6:跟随重定向。 默认 curl 停在 301/302 就返回,加 -L 跟到底:
curl -L https://example.com/old-path
场景 7:把响应存文件而不是终端。 -o 指定文件名,-O 用远端文件名保存:
# 下载大文件别忘 -L:
curl -LO https://example.com/install.sh
-o 搭配 -s(静默进度)和 -w(输出统计)是脚本里最常用的组合,见场景 14。
场景 8:带认证调用接口。 Basic 认证用 -u,Bearer token 放在头里:
curl -u admin:secret https://httpbin.org/basic-auth/admin/secret
curl -H "Authorization: Bearer eyJhbG..." https://httpbin.org/bearer
场景 9:带 cookie。 响应里的 Set-Cookie 存下来,后续请求带上:
curl -c cookies.txt https://httpbin.org/get # 存
curl -b cookies.txt https://httpbin.org/get # 带
场景 10:把状态码、耗时等指标打到 stdout。 -w 后跟格式串,curl 把内部变量替换成实际值:
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com
这行命令在脚本里做接口健康检查最常见:状态码加总耗时一行出。-w 是 curl 从调试工具变成监控工具的关键。 常用变量见下表。
| 变量 | 含义 |
|---|---|
%{http_code} | 最后一个响应的状态码 |
%{time_namelookup} | DNS 解析耗时 |
%{time_connect} | TCP 握手耗时 |
%{time_appconnect} | TLS 握手耗时(HTTP) |
%{time_starttransfer} | 从发请求到收到首字节(TTFB) |
%{time_total} | 总耗时 |
%{size_download} | 下载字节数 |
场景 11-15:网络层怎么管
场景 11:看完整请求和响应。 -v 打印整个会话,> 开头是发出去的请求行,< 开头是收到的响应行,* 开头是连接信息:
curl -v https://httpbin.org/get
要写进工单或日志,用 --trace-ascii 文件名,比 -v 更完整。
场景 12:强制覆盖 DNS 解析。 域名还没生效、想提前验证新机器时,--resolve 让 curl 把指定域名解析到指定 IP,不发真实 DNS 查询:
curl --resolve api.example.com:443:203.0.113.10 https://api.example.com/health
证书校验仍按原域名进行,证书不匹配照样报错——这正是这个场景的价值:测的是新机器对原域名的真实表现,连证书都对齐了。 灰度切流前用它验证新机器,比改 hosts 文件干净。
场景 13:走代理。 -x 指定代理,协议写在代理地址前:
curl -x http://proxy.internal:8080 https://example.com
curl -x socks5h://127.0.0.1:1080 https://example.com
socks5h 的 h 表示域名也走代理解析,服务器本身解析不了的域名用它。
场景 14:限时与重试。 脚本里调接口必须设超时和重试,否则一个卡住的请求能挂住整个 cron:
curl --connect-timeout 5 -m 30 --retry 3 --retry-delay 2 \
--retry-all-errors -f -s -o /dev/null https://example.com/health
| 参数 | 含义 |
|---|---|
--connect-timeout 5 | TCP 连接阶段最多等 5 秒 |
-m 30 | 整个请求(含传输)最多 30 秒 |
--retry 3 | 失败重试 3 次(默认只重试瞬时错误) |
--retry-all-errors | 任何错误都重试(需 curl 7.71+) |
-f | HTTP 状态码 ≥ 400 时返回非零退出码 |
-s | 隐藏进度条和错误信息 |
-f 是脚本场景的关键参数:没有它,curl 收到 500 也会正常退出 0,脚本把失败当成功。
场景 15:压测前先单发,模拟真实客户端。 -A 改 User-Agent,-e 改 Referer,--compressed 声明支持压缩:
curl -A "Mozilla/5.0" -e "https://example.com/page" --compressed \
https://httpbin.org/get
这些头影响服务端缓存和 CDN 行为,测试环境模拟线上客户端时靠它们对齐。
一个可直接贴进 crontab 的健康检查
把场景 10 和 14 组合,就是一个最小可用的接口巡检脚本:
#!/usr/bin/env bash
set -euo pipefail
URL="https://example.com/health"
code=$(curl -s -o /dev/null -w '%{http_code}' \
--connect-timeout 5 -m 30 --retry 2 --retry-delay 2 \
--retry-all-errors -f "$URL")
echo "$(date '+%F %T') $URL $code"
-f 保证 5xx 会触发重试,重试耗尽退出非零,set -e 接住非零退出,cron 的邮件通知能收到。这十几行替代了大部分「接口挂了才发现」的场景。
场景速查表
| 需求 | 命令 |
|---|---|
| GET | curl URL |
| POST JSON | curl -X POST -H "Content-Type: application/json" -d @body.json URL |
| 只看响应头 | curl -I URL |
| 跟随重定向 | curl -L URL |
| 状态码+耗时 | curl -s -o /dev/null -w '%{http_code} %{time_total}\n' URL |
| 完整调试 | curl -v URL |
| 覆盖 DNS | curl --resolve host:443:IP URL |
| 代理 | curl -x socks5h://127.0.0.1:1080 URL |
| 限时重试 | curl -f -s -o /dev/null --connect-timeout 5 -m 30 --retry 3 URL |
常见问题
Q:curl -d 发 JSON,服务端收到的是乱码或字段缺失?
检查引号。双引号会让 shell 展开 $ 和反引号,JSON 内容被篡改。body 一律用单引号包,或用 -d @file.json 从文件读。
Q:脚本里 curl 返回 0,但接口明明返回了 500?
curl 默认把 HTTP 错误状态码当「成功送达」处理,退出码 0。加 -f,状态码 ≥ 400 时退出码非零,脚本判断才准。
Q:-v 的输出里 >、<、* 分别是什么?
> 开头是 curl 发出的请求行和头,< 是服务端返回的响应行和头,* 是 TLS、连接层信息。排查「请求到底发出去了什么」看 > 行。
Q:域名还没切 DNS,想先测新服务器?
--resolve 域名:443:IP 让 curl 直连指定 IP,Host 头和 TLS SNI 仍是原域名,证书校验照常进行。比改系统 hosts 干净,不用 root 权限。
Q:HTTPS 报证书错误,-k 关掉校验行不行?
-k(--insecure)跳过证书校验,调试自签名证书的测试环境可以临时用,生产排查别养成习惯。要看具体证书内容用 -v 输出里的 TLS 段。
命令速记
15 个场景收敛下来,日常高频的就这几件事:-d 发体、-I 看头、-L 跟重定向、-v 看全程、-w 出指标、--resolve 盖 DNS、超时重试加 -f。把这七个动作记住,九成接口调试不需要打开图形工具。
服务器上的 curl 版本一般落后本地一两年,行为差异集中在新增参数上。遇到「本地能跑服务器不能跑」,先 curl --version,再查参数是不是新版本引入的。