curl —— 命令行 HTTP 客户端

运维工具手册 03-文件与传输 6.1 KB 原文

curl —— 命令行 HTTP 客户端

分类:03 文件与传输 适用:全平台(libcurl),几乎所有发行版预装 一句话:验证接口、排查 HTTP 问题、下载上传,一条命令搞定。

1. 它解决什么问题

  • 接口返回 502/403,到底是网关、证书还是应用的问题?
  • 需要带自定义 header、Cookie、证书、代理访问。
  • 上传文件、调 REST API、看完整请求响应过程。
  • 精确测出 DNS、连接、TLS、首字节各阶段耗时。

2. 基础用法

curl https://example.com                     # 输出到 stdout
curl -o page.html https://example.com        # 存文件
curl -O https://example.com/file.tar.gz      # 用远端文件名
curl -s https://api.example.com/data         # 静默(不显示进度)
curl -sS https://api.example.com/data        # 静默但显示错误(常用)
curl -L https://example.com                  # 跟随重定向
curl -I https://example.com                  # 只看响应头(HEAD)
curl -i https://example.com                  # 响应头 + body
curl -v https://example.com                  # 完整过程(含握手)

3. 请求构造

# GET 带参数(注意引号,避免 & 被 shell 解释)
curl -s 'https://api.example.com/search?q=redis&page=2'

# POST JSON
curl -s -X POST https://api.example.com/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"tom","age":18}'

# 表单
curl -s -X POST https://api.example.com/login -d 'user=tom&pass=123'

# URL 编码(避免手写 %20)
curl -s -G https://api.example.com/search --data-urlencode 'q=redis 集群'

# 上传文件(multipart)
curl -s -F 'file=@/tmp/app.log' -F 'type=log' https://api.example.com/upload

# PUT 上传
curl -s -T /tmp/big.bin https://api.example.com/upload/big.bin

# 自定义方法 + 无 body
curl -s -X DELETE https://api.example.com/users/1
curl -s -X OPTIONS -i https://api.example.com/users

# 常用头
curl -s -H 'Authorization: Bearer <token>' \
     -H 'Accept: application/json' \
     -H 'X-Request-Id: 12345' \
     https://api.example.com/me

4. 认证与证书

curl -u user:pass https://example.com              # Basic Auth
curl --digest -u user:pass https://example.com     # Digest
curl -k https://self-signed.example.com            # 跳过证书校验(仅测试)
curl --cacert /etc/ssl/ca.crt https://internal.example.com   # 指定 CA
curl --cert client.crt --key client.key https://mtls.example.com   # 双向 TLS
curl --cert-type P12 --cert client.p12:password https://mtls.example.com

5. 连接与超时

curl --connect-timeout 5 --max-time 30 https://example.com
curl --retry 3 --retry-delay 2 --retry-connrefused https://example.com
curl --limit-rate 1m -O https://example.com/big.iso     # 限速
curl -C - -O https://example.com/big.iso                # 断点续传
curl --resolve api.example.com:443:10.0.0.5 https://api.example.com   # 绕过 DNS 指定 IP
curl -x http://proxy:8080 https://example.com           # 走代理
curl -x socks5h://127.0.0.1:1080 https://example.com
curl --http1.1 https://example.com                      # 强制 HTTP/1.1
curl --http2 https://example.com
curl -4 / -6 https://example.com                        # 强制 IPv4/IPv6

--resolve 是排查"DNS 解析到旧 IP"最有用的参数:不改 hosts 就能验证新机器。

6. 输出控制(排障关键)

# 只取状态码
curl -s -o /dev/null -w '%{http_code}\n' https://example.com

# 分阶段耗时
curl -s -o /dev/null -w '
dns:      %{time_namelookup}
connect:  %{time_connect}
tls:      %{time_appconnect}
ttfb:     %{time_starttransfer}
total:    %{time_total}
code:     %{http_code}
size:     %{size_download}
ip:       %{remote_ip}
' https://example.com
变量 含义 异常指向
time_namelookup DNS 解析耗时 大 → DNS 问题
time_connect TCP 握手完成 与 namelookup 差值大 → 网络 RTT/防火墙
time_appconnect TLS 握手完成 大 → 证书链、TLS 版本、OCSP
time_starttransfer 首字节 大 → 服务端处理慢
time_total 总耗时
remote_ip 实际连接 IP 确认是否走了预期节点
size_download 下载字节

一键健康检查脚本:

curl -s -o /dev/null -w '%{http_code} %{time_total}s %{remote_ip}\n' "$1"

7. 实战场景

7.1 判断 502 是谁返回的

curl -sI https://api.example.com/health
# Server: nginx  → 网关自己返的,上游挂了
# Server: nginx/1.24.0 + X-Powered-By → 应用返回
curl -sv https://api.example.com/health 2>&1 | head -40

看 Server、X-Powered-By、Via、X-Cache 等头判断链路。

7.2 验证证书与 TLS

curl -vI https://example.com 2>&1 | grep -E 'SSL|TLS|subject|expire|issuer'
curl -s --cert-status https://example.com        # 检查 OCSP stapling
curl -sI --tlsv1.2 https://example.com           # 强制某版本

7.3 带 Cookie 的会话调试

curl -c /tmp/cookie.txt -d 'user=tom&pass=123' https://example.com/login
curl -b /tmp/cookie.txt https://example.com/profile

7.4 下载并校验

curl -fL -o app.tar.gz https://example.com/app.tar.gz || echo "下载失败"
sha256sum app.tar.gz

-f(--fail)让 HTTP 4xx/5xx 返回非 0 退出码,否则 curl 会把错误页当成功保存。

7.5 压测前的连通性基线

for i in $(seq 1 10); do
  curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://api.example.com/ping
done

先确认单请求正常,再上 ab/wrk,否则压测结果没有意义。

7.6 上传到对象存储(预签名 URL)

curl -s -X PUT -T backup.tar.gz \
  -H 'Content-Type: application/octet-stream' \
  'https://bucket.oss-cn-hangzhou.aliyuncs.com/backup.tar.gz?Signature=...'

8. 常见坑

  1. -d 默认就是 POST,再写 -X POST 冗余;但 -X GET -d 会发送带 body 的 GET,很多服务端直接拒绝。
  2. -L 与 -X 冲突:重定向后 curl 仍用指定方法,301 后本该 GET 却继续 POST,导致 405。用 --post301 --post302 显式控制。
  3. -I 发的是 HEAD:有些服务不支持 HEAD 返回 405,用 -s -D - -o /dev/null 取头。
  4. -k 会掩盖真实证书问题:生产排障先不加 -k,看清楚错在哪。
  5. URL 里的 & 必须引号包住,否则 shell 后台执行,命令被截断。
  6. -o 与 -O 混用:-O 用远端名,配 --output-dir 更好。
  7. 不加 -f 下载失败也返回 0:脚本里必须 -f。
  8. 大文件默认全在内存:用 -o 落盘,不要管道给变量。
  9. HTTP/2 下 header 名被小写,抓包/日志比对时注意。
  10. 代理环境变量会干扰:http_proxy/https_proxy/no_proxy 影响 curl,排查时用 --noproxy '*'。
  11. --resolve 只影响 DNS,SNI 仍是 URL 里的域名,这正是它的价值。
  12. -w 里的换行:在 -w 字符串中用真实换行或 \n,注意 shell 引号类型。

9. 速查表

curl -sS <url>                                   # 静默+报错
curl -sI <url>                                   # 响应头
curl -sv <url>                                   # 全过程
curl -L -o file <url>                            # 跟随重定向下载
curl -fL -O <url>                                # 失败即错误
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' <url>
curl -X POST -H 'Content-Type: application/json' -d '{...}' <url>
curl -F 'file=@/path' <url>                       # 上传
curl -u user:pass <url>                           # Basic
curl --resolve host:443:1.2.3.4 https://host/     # 指定 IP
curl -x http://proxy:8080 <url>                   # 代理
curl -C - -O <url>                                # 断点续传
curl --connect-timeout 5 --max-time 30 <url>
curl —— 命令行 HTTP 客户端 - 知识库 - 起航社区