curl 命令详解与参数实测(Ubuntu 22.04)

curl 7.81.0 全参数实测:默认输出与 -s/-S 静默策略、-o/-O 落盘、-sI 响应头逐项解读、-v 调试三段结构、-L 重定向跟随与 num_redirects、-d/-X POST 与 JSON 头体、-H 自定义头、-u Basic 认证 401/200 双形态、-x 代理 rc=7、-w 分段耗时指标、--max-time rc=28、-C - 断点续传 md5 校验、-k 自签证书 rc=60、-A/-e 请求伪装与退出码 6/7/22/28 对照

Linux命令Linux系统UbuntuUbuntu 22.04命令行网络工具2026/10/16

curl 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 curl 的每一个常用参数——默认输出与 -s/-S 静默策略、-o/-O 落盘方式、-I 响应头逐项解读、-i 头体同显、-v 调试三段结构、-L 重定向跟随、-d/-X POST 请求、-H 自定义头、-u Basic 认证、-x 代理、-w 格式化指标、--max-time 超时、-C - 断点续传、-k 自签证书、-A/-e 请求伪装,以及退出码 6/7/22/28 的错误形态对照,全部命令与回显已在 Ubuntu 22.04(curl 7.81.0)实测通过。

一、环境信息

本节列出本文所有实测所依赖的环境、测试数据与本地服务,便于读者按同规格复现。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • curl 版本:curl 7.81.0(Release-Date: 2022-01-05,Ubuntu 22.04 官方仓库默认版本,curl --version 实测)
  • 公网目标:httpbin.org(可正常访问,用于重定向/POST/认证等回显服务)、www.baidu.com
  • 本地测试服务(用 Python 的 http.server 模块模拟,均监听在 127.0.0.1):
服务端口用途
文件服务(支持 Range 请求,返回 Accept-Ranges: bytes)8000默认下载、-I/-i 头解读、-O、-w、-C - 断点续传
慢响应服务(收到请求后延迟 5 秒再应答)8001--max-time 超时实测
自签 HTTPS 服务(证书 CN=localhost)8443-k 跳过证书校验实测
  • 演示目录:/root/demo/curl/,测试数据清单:
文件大小用途
site/index.html356 字节HTML 页面(默认输出演示)
site/hello.txt31 字节小文本下载目标
site/big.bin10,485,760 字节(10 MiB)断点续传
site/medium.bin3,145,728 字节(3 MiB)-w 速率统计
site/data.gz、site/notes.txt、site/style.css、site/img/logo.txt各数十字节组合场景素材
cert.pem / key.pem自签证书对8443 端口 TLS 服务

💡 技术拓展:curl 与 wget 都是命令行传输工具,选型分工对比如下:

维度curlwget
定位「传数据」的瑞士军刀,协议多达 25 种(HTTP/FTP/SMTP/SFTP…)专精 HTTP/FTP 的「下载器」
递归下载不支持原生支持(-r 递归抓整站)
上传强项(-T、-d、-F 表单)仅 FTP 上传
API 调试强项(-v/-H/-X/-u 细粒度控制)基本不支持
断点续传-C --c(对本地文件更智能)
默认行为输出到标准输出直接落盘

简单记忆:调接口用 curl,下东西/搬站用 wget。本文是 curl 专题,wget 的对应实测见本专栏《wget 命令详解与参数实测(Ubuntu 22.04)》。

二、命令概览

2.1 语法与测试数据准备

curl 的语法是 curl [options...] <url>,不给任何选项时它把 URL 的响应体原样打印到标准输出——这既是它作为「管道友好」工具的优点,也让初学者在终端里被整屏 HTML 与进度表吓到。本文所有实测基于一个本地小站点与 httpbin.org 公网回显服务。

准备演示数据与本地文件服务(http.server 默认监听 8000 端口,以 site/ 为站点根):

$ mkdir -p /root/demo/curl/site/img /root/demo/curl/dl && cd /root/demo/curl && printf 'hello curl\nline two\nline three\n' > site/hello.txt && printf 'notes text\n' > site/notes.txt && echo gzcontent > site/data.gz && echo logo > site/img/logo.txt
$ dd if=/dev/urandom of=/root/demo/curl/site/big.bin bs=1M count=10 status=none && dd if=/dev/urandom of=/root/demo/curl/site/medium.bin bs=1M count=3 status=none && ls -l /root/demo/curl/site
total 13340
-rw-r--r-- 1 root root 10485760 Oct 15 19:56 big.bin
-rw-r--r-- 1 root root       10 Oct 15 19:56 data.gz
-rw-r--r-- 1 root root       31 Oct 15 19:56 hello.txt
drwxr-xr-x 2 root root     4096 Oct 15 19:56 img
-rw-r--r-- 1 root root      356 Oct 15 20:16 index.html
-rw-r--r-- 1 root root  3145728 Oct 15 19:56 medium.bin
-rw-r--r-- 1 root root       11 Oct 15 19:56 notes.txt
-rw-r--r-- 1 root root      212 Oct 15 20:16 page2.html
-rw-r--r-- 1 root root       34 Oct 15 19:56 style.css

启动文件服务并验证(服务进程挂在后台,日志落 /tmp/http8000.log):

$ cd /root/demo/curl/site && nohup python3 -m http.server 8000 --bind 0.0.0.0 >/tmp/http8000.log 2>&1 &
$ sleep 1 && curl -s http://localhost:8000/hello.txt
hello curl
line two
line three

💡 说明:python3 自带的 http.server 不支持 Range 分块请求,而 3.14 的断点续传需要服务器配合返回 206 Partial Content。实测时在 8000 端口换用了带 Range 支持的简易 Python 服务(响应头会多一行 Accept-Ranges: bytes,见 3.3/3.4 的真实回显);读者复现时用 nginx 等常规 Web 服务器也能得到同样形态的响应头。

2.2 --help 输出(分类式菜单,节选主干)

curl 7.81 的 --help 改成了分类菜单式,只显示最常用的 12 个选项并提示分类入口,全量回显如下:

$ curl --help | head -45
Usage: curl [options...] <url>
 -d, --data <data>          HTTP POST data
 -f, --fail                 Fail silently (no output at all) on HTTP errors
 -h, --help <category>      Get help for commands
 -i, --include              Include protocol response headers in the output
 -o, --output <file>        Write to file instead of stdout
 -O, --remote-name          Write output to a file named as the remote file
 -s, --silent               Silent mode
 -T, --upload-file <file>   Transfer local FILE to destination
 -u, --user <user:password> Server user and password
 -A, --user-agent <name>    Send User-Agent <name> to server
 -v, --verbose              Make the operation more talkative
 -V, --version              Show version number and quit

This is not the full help, this menu is stripped into categories.
Use "--help category" to get an overview of all categories.
For all options use the manual or "--help all".

全量选项要 --help all,实测共 245 行,以下节选开头 30 行(字母序排列的长选项全貌):

$ curl --help all | wc -l
245
$ curl --help all | sed -n '1,30p'
Usage: curl [options...] <url>
     --abstract-unix-socket <path> Connect via abstract Unix domain socket
     --alt-svc <file name> Enable alt-svc with this cache file
     --anyauth            Pick any authentication method
 -a, --append             Append to target file when uploading
     --aws-sigv4 <provider1[:provider2[:region[:service]]]> Use AWS V4 signature authentication
     --basic              Use HTTP Basic Authentication
     --cacert <file>      CA certificate to verify peer against
     --capath <dir>       CA directory to verify peer against
 -E, --cert <certificate[:password]> Client certificate file and password
     --cert-status        Verify the status of the server cert via OCSP-staple
     --cert-type <type>   Certificate type (DER/PEM/ENG)
     --ciphers <list of ciphers> SSL ciphers to use
     --compressed         Request compressed response
     --compressed-ssh     Enable SSH compression
 -K, --config <file>      Read config from a file
     --connect-timeout <fractional seconds> Maximum time allowed for connection
     --connect-to <HOST1:PORT1:HOST2:PORT2> Connect to host
 -C, --continue-at <offset> Resumed transfer offset
 -b, --cookie <data|filename> Send cookies from string/file
 -c, --cookie-jar <filename> Write cookies to <filename> after operation
     --create-dirs        Create necessary local directory hierarchy
     --create-file-mode <mode> File mode for created files
     --crlf               Convert LF to CRLF in upload
     --crlfile <file>     Use this CRL list
     --curves <algorithm list> (EC) TLS key exchange algorithm(s) to request
 -d, --data <data>        HTTP POST data
     --data-ascii <data>  HTTP POST ASCII data
     --data-binary <data> HTTP POST binary data
     --data-raw <data>    HTTP POST data, '@' allowed

💡 说明:--help all 245 行覆盖了 curl 的全部能力面;按类别检索可先 curl --help 看类别名,再 curl --help <category> 缩小范围。本文第三章只实测日常高频参数。

2.3 版本信息(特性行解读)

curl --version 输出三行:主行、发布日期、协议与特性清单,逐段解读价值很大:

$ curl --version
curl 7.81.0 (x86_64-pc-linux-gnu) libcurl/7.81.0 OpenSSL/3.0.2 zlib/1.2.11 brotli/1.0.9 zstd/1.4.8 libidn2/2.3.2 libpsl/0.21.0 (+libidn2/2.3.2) libssh/0.9.6/openssl/zlib nghttp2/1.43.0 librtmp/2.3 OpenLDAP/2.5.20
Release-Date: 2022-01-05
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM NTLM_WB PSL SPNEGO SSL TLS-SRP UnixSockets zstd
  • 主行:curl 与 libcurl 版本 7.81.0、平台三元组 x86_64-pc-linux-gnu,后跟各后端库(TLS 用 OpenSSL 3.0.2、压缩支持 brotli/zstd、SSH 用 libssh、HTTP/2 用 nghttp2);
  • Protocols 行:该构建支持的 25 种协议,写脚本前先确认目标协议在列(如 scp/sftp 就依赖 libssh);
  • Features 行:HTTP2 表示支持 h2、AsynchDNS 表示 DNS 解析异步(不阻塞并发)、Largefile 支持大于 2 GiB 文件、SSL 支持 HTTPS、IDN 支持国际化域名。

💡 要点:排障时第一件事就是 curl --version——HTTPS 报错先看有没有 SSL 特性,HTTP/2 问题先看有没有 HTTP2 特性,不同发行版裁剪出的能力差异很大。

三、参数逐项实测

3.1 默认输出与 -s 静默

curl 默认把响应体打印到标准输出,配合管道可截取任意片段。-s(silent)同时抑制进度表和错误消息,是脚本场景的标准起手式:

$ curl -s http://localhost:8000/ | head -8
<!DOCTYPE html>
<html><head><title>Demo Site</title><link rel="stylesheet" href="http://localhost:8000/style.css"></head>
<body><h1>Hello Demo Site</h1>
<p><a href="http://localhost:8000/page2.html">Page 2</a></p>
</body></html>

$ curl -s http://localhost:8000/hello.txt
hello curl
line two
line three

💡 要点:非交互(输出重定向)场景下 curl 的进度表一般不刷新,但 HEAD 形态(-I 不带 -s)实测仍会输出一帧统计;-s 能把这类噪音一并关掉。注意 -s 连错误消息也吞掉,排障时要用 -sS(见 3.5)。

3.2 -o 写入指定文件 / -O 按远端名保存

-o 自定义本地文件名,-O 直接用 URL 路径里的文件名落地(URL 必须带文件名部分,否则报错):

$ cd /root/demo/curl && curl -s -o saved-index.html http://localhost:8000/; echo rc=$?; ls -l saved-index.html
rc=0
-rw-r--r-- 1 root root 229 Oct 15 19:56 saved-index.html

$ cd /root/demo/curl/dl && curl -s -O http://localhost:8000/hello.txt; echo rc=$?; ls -l
rc=0
total 4
-rw-r--r-- 1 root root 31 Oct 15 19:56 hello.txt

💡 要点:-o /dev/null 是「只测不通、不留文件」的经典写法,配合 -w 输出指标(见 3.12);批量下载时 -O 与 --remote-name-all 搭配可一次拉多个 URL。注意 -O 遇到以 / 结尾的 URL 会失败,因为提取不出文件名。

3.3 -I 只取响应头(逐头解读)

-I 发出 HEAD 请求,只返回响应头。这是「接口健康检查」最常用的一招:

$ curl -sI http://localhost:8000/
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.12
Date: Thu, 15 Oct 2026 20:26:38 GMT
Content-type: text/html
Content-Length: 356
Last-Modified: Thu, 15 Oct 2026 20:16:43 GMT

逐头解读:

  • HTTP/1.0 200 OK:协议版本 + 状态码 + 短语。SimpleHTTP 按 HTTP/1.0 应答,生产服务器通常是 HTTP/1.1 200 OK 或 HTTP/2 200;
  • Server: SimpleHTTP/0.6 Python/3.10.12:服务器软件指纹(安全加固时常把这里改掉);
  • Date: Thu, 15 Oct 2026 20:26:38 GMT:服务器响应时间,一律 GMT 时区;
  • Content-type: text/html:正文 MIME 类型,注意这个服务把该头写成小写 Content-type(HTTP 头名大小写不敏感);
  • Content-Length: 356:正文字节数,脚本可用来做文件大小预检;
  • Last-Modified: Thu, 15 Oct 2026 20:16:43 GMT:文件最后修改时间,配合 If-Modified-Since 做缓存协商。

💡 要点:不带 -s 的 curl -I 在非交互输出下实测仍会额外打印一帧进度表统计(% Total / % Received ... 两行),加 -s 即可消除。-I 适合探测存在性、跳转、缓存策略而不下载正文;但部分服务器对 HEAD 与 GET 行为不一致,需要精确复现 GET 时用 -s -o /dev/null -w '%{http_code}'。

3.4 -i 响应头 + 响应体一起输出

-i(include)在正文前面带上响应头,适合一次性确认「头与体」的对应关系:

$ curl -s -i http://localhost:8000/hello.txt
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.10.12
Date: Thu, 15 Oct 2026 19:56:40 GMT
Content-Type: application/octet-stream
Content-Length: 31
Accept-Ranges: bytes

hello curl
line two
line three

💡 要点:-i 与 -I 的区别一目了然——-i 是 GET 请求带响应头输出,-I 是 HEAD 请求只有响应头。注意此回显里 Accept-Ranges: bytes 表示服务器支持分块请求,这正是 3.14 断点续传能工作的前提。

3.5 -s 吞错误与 -S 恢复错误显示

-s 静默模式会连 curl 自己的错误消息一起吞掉,只留下退出码;加 -S(show-error)可在静默的同时保留错误消息——这是脚本里「安静但可排障」的正确组合:

$ curl -s http://no-such.invalid; echo rc=$?
rc=6
$ curl -sS http://no-such.invalid; echo rc=$?
curl: (6) Could not resolve host: no-such.invalid
rc=6

💡 要点:生产脚本推荐固定写法 curl -sS ...; rc=$?——成功时零噪音,失败时错误进日志、退出码进监控。

3.6 -v 调试(* 信息 / > 请求 / < 响应 三段)

-v 是 curl 最强排障参数,输出按前缀分三类:* 是连接/协议层信息,> 是发出的请求行与请求头,< 是收到的状态行与响应头:

$ curl -sv http://localhost:8000/hello.txt 2>&1 | head -30
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /hello.txt HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.10.12
< Date: Thu, 15 Oct 2026 19:56:40 GMT
< Content-Type: application/octet-stream
< Content-Length: 31
< Accept-Ranges: bytes
<
{ [31 bytes data]
* Closing connection 0
hello curl
line two
line three

💡 要点:三段结构正好对应一次 HTTP 交互的三个阶段——连上了吗(*)、发了什么(>)、收到什么(<)。-v 输出走 stderr,所以示例里用 2>&1 合并;要更细的十六进制轨迹用 --trace-ascii 文件。

3.7 -L 跟随重定向

默认 curl 收到 3xx 就停,-L 让它带着新地址继续请求(最多 50 跳)。先看不做跟随时的 302 原始响应:

$ curl -sI -m 15 http://httpbin.org/redirect/2 | head -6
HTTP/1.1 302 FOUND
Date: Thu, 15 Oct 2026 19:56:42 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 247
Connection: keep-alive
Server: gunicorn/19.9.0

加 -L 后跟随 2 跳到达终点 /get,配合 -w 变量验证:

$ curl -s -o /dev/null -m 15 -w 'final=%{url_effective} code=%{http_code} redirects=%{num_redirects}\n' -L http://httpbin.org/redirect/2
final=http://httpbin.org/get code=200 redirects=2

💡 要点:-L 的最佳搭档是 -w 的三个变量:url_effective(最终 URL)、http_code(最终状态码)、num_redirects(跳了几次)。排查「跳转后丢了 POST 方法」问题时,记住 curl 默认对 301/302 会把 POST 降级为 GET,--post301/--post302 可保持。

3.8 -d POST 表单与 -X POST 显式指定方法

-d 会自动把请求方法设为 POST,并默认发送 application/x-www-form-urlencoded 表单(httpbin.org 的 /post 会把收到的请求原样回显成 JSON):

$ curl -s -m 15 -d 'name=qzuo&score=99' http://httpbin.org/post | head -12
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "name": "qzuo",
    "score": "99"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "18",
    "Content-Type": "application/x-www-form-urlencoded",

-X POST 只显式指定方法,配合 -H 改 Content-Type 即可发 JSON:

$ curl -s -m 15 -X POST -H 'Content-Type: application/json' -d '{"city":"beijing"}' http://httpbin.org/post | head -10
{
  "args": {},
  "data": "{\"city\":\"beijing\"}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Content-Length": "18",
    "Content-Type": "application/json",
    "Host": "httpbin.org",

💡 要点:-d 数据里出现 @file 时会读文件内容作为请求体(-d @body.json),想原样发送 @ 用 --data-raw。-X 本身不改变语义,只强制方法名——没有 -d 的 -X POST 发出的是空 body 的 POST,初学者常因此踩坑。

3.9 -H 自定义请求头

-H '名字: 值' 逐个添加请求头,可重复多次;httpbin.org 的 /headers 会回显收到的全部请求头:

$ curl -s -m 15 -H 'X-Trace-Id: trace-001' -H 'Accept: application/json' http://httpbin.org/headers
{
  "headers": {
    "Accept": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.81.0",
    "X-Amzn-Trace-Id": "Root=1-6ad12fff-41ed3b1443f5fef45d0a3a26",
    "X-Trace-Id": "trace-001"
  }
}

💡 要点:-H 也能用来覆盖默认头(如 -H 'User-Agent: xxx' 等价 -A)或删除头(-H 'Host:' 去掉 Host)。调试网关鉴权时,-H "Authorization: Bearer <token>" 是与 -u Basic 并列的常用形态。

3.10 -u Basic 认证(200 与 401 双形态)

-u 用户:密码 发送 HTTP Basic 认证。用 httpbin.org 的 /basic-auth/<user>/<passwd> 验证:凭据正确返回认证成功 JSON:

$ curl -s -m 15 -u admin:q1w2e3r4 https://httpbin.org/basic-auth/admin/q1w2e3r4
{
  "authenticated": true,
  "user": "admin"
}

凭据错误时是标准的 401 形态(响应体几乎为空,靠状态码判断):

$ curl -s -m 15 -o /dev/null -w 'code=%{http_code}\n' -u admin:wrong-pass https://httpbin.org/basic-auth/admin/q1w2e3r4
code=401

💡 要点:不传 -u 直接请求受保护资源,会得到 401 与 WWW-Authenticate 头(可用 -i 看到);加 -u 后 curl 自动完成 Basic 编码。密码在命令行明文可见,脚本里更稳妥的写法是 -u admin 让 curl 交互式提示输入,或用 --netrc。

3.11 -x 走代理(无可用代理时的报错形态)

-x [协议://]主机:端口 指定代理。实测指向一个不存在的代理端口(127.0.0.1:1),观察失败形态:

$ curl -sS -x http://127.0.0.1:1 http://localhost:8000/hello.txt; echo rc=$?
curl: (7) Failed to connect to 127.0.0.1 port 1 after 0 ms: Connection refused
rc=7

💡 要点:-x 后的地址是代理的地址,目标 URL 不变;curl 会先与代理建连再转发目标请求(HTTPS 目标走 CONNECT 隧道)。反向记忆:错误消息里 "Failed to connect to <代理IP> port <代理端口>" 就说明卡在代理握手这一步。内网直连场景若环境变量 http_proxy 被误设,可用 --noproxy '*' 临时绕开。

3.12 -w 格式化输出(http_code / time_total / size_download)

-w '模板' 在传输结束后按模板输出指标,配合 -o /dev/null 组成「零落盘测速器」。实测本地 3 MiB 文件:

$ curl -s -o /dev/null -w 'http_code=%{http_code} time_total=%{time_total}s size_download=%{size_download} speed_download=%{speed_download}B/s\n' http://localhost:8000/medium.bin
http_code=200 time_total=0.001439s size_download=3145728 speed_download=2186051424B/s

💡 要点:-w 支持 50+ 变量,高频的有 %{http_code}、%{time_total}(总耗时)、%{time_namelookup}(DNS 解析耗时)、%{time_connect}(TCP 建连耗时)、%{time_starttransfer}(首字节耗时,TTFB)、%{size_download}、%{speed_download}。分段看耗时就能定位「慢在 DNS、慢在建连还是慢在服务端」——回环环境 time_total 仅 1.4ms,公网目标通常在几十到几百 ms 量级。

3.13 --max-time 超时截断(rc=28)

--max-time 限制整次传输的最长秒数(支持小数),超时立刻断开,退出码 28。对延迟 5 秒的本地慢响应服务实测:

$ curl -s --max-time 2 http://localhost:8001/slow; echo rc=$?
rc=28

💡 要点:--max-time 管「整次传输」(DNS+建连+传输全程),--connect-timeout 只管「TCP 建连」。生产脚本两者都要设:curl --connect-timeout 3 --max-time 10,避免对端挂起时脚本无限等待。rc=28 与 rc=7 的区别是「连上了但没传完」与「根本没连上」。

3.14 -C - 断点续传(Range 实测)

-C - 让 curl 从本地已有文件的大小处继续下载(自动发 Range: bytes=<offset>- 请求)。先模拟「下了一半」——用 -r 只取前 2 MiB:

$ cd /root/demo/curl && curl -s -r 0-2097151 -o part.bin http://localhost:8000/big.bin; echo rc=$?; ls -l part.bin
rc=0
-rw-r--r-- 1 root root 2097152 Oct 15 19:56 part.bin

-C - 续传剩余 8 MiB,一次到位:

$ cd /root/demo/curl && curl -s -C - -o part.bin http://localhost:8000/big.bin; echo rc=$?; ls -l part.bin site/big.bin
rc=0
-rw-r--r-- 1 root root 10485760 Oct 15 19:56 part.bin
-rw-r--r-- 1 root root 10485760 Oct 15 19:56 site/big.bin

md5 校验证明续传结果与源文件逐字节一致:

$ cd /root/demo/curl && md5sum part.bin site/big.bin
760c6c33bdb859bafacf7959a42ef5b0  part.bin
760c6c33bdb859bafacf7959a42ef5b0  site/big.bin

💡 要点:断点续传三要素——服务器支持 Range(响应头 Accept-Ranges: bytes)、中断时保留半截文件、续传用 -C -。若服务器不支持 Range,curl 会报 (33) HTTP server doesn't seem to support byte ranges,此时只能整文件重下。

3.15 -k 跳过证书校验(自签 HTTPS 实测)

对自签证书的 HTTPS 服务,默认校验会失败(rc=60),-sS 拿到完整错误消息:

$ curl -sS https://localhost:8443/hello.txt; echo rc=$?
curl: (60) SSL certificate problem: self-signed certificate
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
rc=60

加 -k 跳过校验后正常通信:

$ curl -sk https://localhost:8443/hello.txt; echo rc=$?
hello curl
line two
line three
rc=0

💡 要点:-k(--insecure)等于关闭中间人防护,只用于自签环境的临时调试。生产做法是把自签 CA 装入信任链(--cacert my-ca.pem)。rc=60 是证书无法验证,rc=51/35 则分别是证书校验其他失败与 SSL 握手失败,排障时先看错误号。

3.16 -A 自定义 User-Agent 与 -e 自定义 Referer

-A 改 User-Agent、-e 改 Referer,两个最常见的「请求伪装」参数,同样由 httpbin.org 回显验证:

$ curl -s -m 15 -A 'QzuoBot/1.0' http://httpbin.org/user-agent
{
  "user-agent": "QzuoBot/1.0"
}

$ curl -s -m 15 -e 'https://www.example.com/source' http://httpbin.org/headers | grep -i referer
    "Referer": "https://www.example.com/source",

💡 要点:部分网站对默认 UA curl/7.81.0 做拦截,-A 'Mozilla/5.0' 可绕开;-e 用于调试防盗链。两者都等价于 -H 的对应头(-H 'User-Agent: ...' / -H 'Referer: ...'),只是语义化更好。

3.17 退出码对照(6 / 7 / 22 / 28 节选)

curl 退出码有近百个,脚本高频使用的是下面四个,实测形态汇总:

退出码含义实测触发命令实测错误消息
6无法解析主机curl -sS http://no-such.invalidcurl: (6) Could not resolve host: no-such.invalid
7连接被拒/连不上curl -sS http://127.0.0.1:9curl: (7) Failed to connect to 127.0.0.1 port 9 after 0 ms: Connection refused
22HTTP 页面错误(配合 -f)curl -sf http://localhost:8000/nothing(静默,rc=22)
28操作超时curl -s --max-time 2 http://localhost:8001/slow(静默,rc=28)

逐条实测回显(6 号已在 3.5、7 号已在 3.11、28 号已在 3.13 出现):

$ curl -s http://127.0.0.1:9; echo rc=$?
rc=7

$ curl -sf http://localhost:8000/nothing; echo rc=$?
rc=22

💡 要点:-f(fail)让 4xx/5xx 不输出错误页正文、直接以 rc=22 退出——脚本判断 HTTP 错误必须加 -f,否则 404 页面也会被当作「成功」写进文件。

四、实战示例

4.1 接口体检:状态码 + 耗时 + 大小一次拿全

健康检查脚本的标准输出形态,一个命令给出全部关键指标:

$ curl -s -o /dev/null -w 'code=%{http_code} dns=%{time_namelookup}s connect=%{time_connect}s ttfb=%{time_starttransfer}s total=%{time_total}s size=%{size_download}\n' http://localhost:8000/index.html
code=200 dns=0.000010s connect=0.000065s ttfb=0.000522s total=0.000570s size=356

(回环环境各段耗时可忽略不计;公网接口的 dns/connect 段通常在 ms 至几十 ms,ttfb 反映服务端处理耗时。)判断逻辑:code 非 2xx/3xx 看 -f 与服务端日志;ttfb 占 total 比例高说明服务端慢;connect 段高说明网络/建连慢。

4.2 JSON 接口自测:方法 + 头 + 体三要素

模拟一次带鉴权头的 JSON 接口调用,验证服务端回显:

$ curl -s -m 15 -X POST -H 'Content-Type: application/json' -H 'Authorization: Bearer token-abc123' -d '{"city":"beijing"}' http://httpbin.org/post | head -10
{
  "args": {},
  "data": "{\"city\":\"beijing\"}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Authorization": "Bearer token-abc123",
    "Content-Length": "18",
    "Content-Type": "application/json",

要点:-X 定方法、-H 定头、-d 定体,三者组合即可覆盖绝大多数 REST 接口自测场景。

4.3 大文件限速下载与断点续传

共享带宽环境下用 --limit-rate 礼貌限速,实测把 3 MiB 文件限到 1 MB/s(总耗时约 3 秒、均速约 1.06 MB/s):

$ curl -s -o /dev/null --limit-rate 1M -w 'code=%{http_code} time_total=%{time_total}s size_download=%{size_download} speed_download=%{speed_download}B/s\n' http://localhost:8000/medium.bin
code=200 time_total=2.956995s size_download=3145728 speed_download=1063825B/s

传输中断后用 -C - 续传收尾(复用 3.14 的两步流程),并用 md5 校验结果完整性:

$ cd /root/demo/curl && curl -s -r 0-2097151 -o part.bin http://localhost:8000/big.bin && curl -s -C - -o part.bin http://localhost:8000/big.bin; echo rc=$?; md5sum part.bin site/big.bin
rc=0
760c6c33bdb859bafacf7959a42ef5b0  part.bin
760c6c33bdb859bafacf7959a42ef5b0  site/big.bin

要点:-C - 前必须保证本地半截文件还在;瞬时网络抖动可交给 --retry 3 --retry-delay 2 自动重试。

4.4 自签 HTTPS 环境临时调试

内网自签服务(管理后台、内部网关)调试时,用 -k 临时绕过校验、-v 看握手细节:

$ curl -sk https://localhost:8443/hello.txt; echo rc=$?
hello curl
line two
line three
rc=0

要点:-k 只解决「证书不可信」;若报 (35) SSL connect error 则是协议/套件不匹配,需要 -v 看握手阶段,并考虑 --tlsv1.2 指定协议版本。

五、注意事项与常见问题

Q1:-s 之后好像什么错误都看不到了? -s 同时抑制进度表与错误消息(见 3.5)。排障改用 -sS,或干脆去掉 -s 看完整输出。

Q2:-X POST 发出去了,服务端却说没收到参数? -X 只改方法名不改请求体。没有 -d/-F 的 POST 是空 body;表单要用 -d 'k=v'(form-urlencoded)或 -F 'k=@file'(multipart),JSON 要同时 -H 'Content-Type: application/json'(见 3.8)。

Q3:404 也被当成成功写进文件了? curl 默认把 4xx/5xx 的错误页正文当普通响应处理(rc=0)。脚本判断 HTTP 状态必须加 -f(rc=22)或显式检查 -w '%{http_code}' 的输出(见 3.17)。

Q4:下载大文件中断了怎么办? 确认服务器支持 Range(curl -sI 看是否有 Accept-Ranges: bytes),保留半截文件后执行 curl -C - -o 文件 URL 续传(见 3.14);服务器不支持 Range 时只能重下。

Q5:自签证书报 (60),能不能长期用 -k? 不能。-k 关闭了服务器身份验证,存在中间人风险。正确做法是 --cacert 指定自签 CA 证书,或将 CA 装入系统信任库(见 3.15)。

Q6:怎么知道慢在哪个环节? 用 -w 的分段耗时变量(见 3.12 与 4.1):dns 段大查 DNS、connect 段大查网络/防火墙、ttfb 段大查服务端处理、total-ttfb 段大查下行带宽。

Q7:-v 的输出太吵,能落盘吗? -v 输出走 stderr,用 curl -v URL 2>debug.log 落盘;需要含十六进制体的完整轨迹用 --trace-ascii debug.log URL。

Q8:退出码速记? 高频四个:6 解析失败(查 DNS/hosts)、7 连不上(查端口/防火墙/代理)、22 HTTP 错误(配合 -f,查服务端)、28 超时(查 --max-time/对端挂起)。完整表见 man curl 的 EXIT CODES 节。

六、参考链接