Skip to content

curl 速查表

用于通过 URL 传输数据的命令行工具。

01

入门

基本请求

curl 从命令行发起 HTTP 请求,默认使用 GET 方法。-v 显示详细输出,-o 保存到指定文件名,-O 保留远程文件名,-L 跟随重定向,-I 仅获取响应头(HEAD 请求)。

curl
# GET request (default)
curl https://example.com

# with verbose output
curl -v https://example.com

# save output to file
curl -o output.html https://example.com
curl -O https://example.com/file.zip   # keep remote filename

# follow redirects
curl -L https://example.com/redirect

# show only headers
curl -I https://example.com
curl --head https://example.com

输出与保存

-o 将响应体写入指定文件;-O 使用远程文件名。--output-dir 设置目标目录。-s(静默)抑制进度条和错误信息——配合 -S 仍可显示错误。使用 -D 将响应头转储到文件,同时用 -o /dev/null 丢弃响应体。

curl
# print body to stdout (default)
curl https://example.com

# save body to file
curl -o page.html https://example.com

# save with remote filename
curl -O https://example.com/file.zip

# save to specific dir with remote name
curl -O --output-dir /tmp https://example.com/file.zip

# append to file
curl https://example.com >> log.txt

# discard body, keep headers
curl -o /dev/null -D headers.txt https://example.com

# silent (no progress, no errors)
curl -s https://example.com

详细输出与响应头

-v(verbose)打印完整的对话过程:'>' 是请求,'<' 是响应,'*' 是 curl 内部信息——全部输出到 stderr,因此 stdout 仍保持干净的响应体。-I 发送 HEAD 请求并打印响应头。-D 将响应头转储到文件或 '-'(表示 stdout);与 -o /dev/null 配合可在不保存响应体的情况下检查响应头。

curl
# verbose: shows request + response, sent to stderr
curl -v https://example.com

# > lines are the request, < lines are the response
# * lines are curl's internal info

# headers only (HEAD request)
curl -I https://example.com

# dump response headers to file
curl -D headers.txt -o body.html https://example.com

# dump headers to stdout, body to file
curl -D - -o body.html https://example.com

# show only response headers (no HEAD)
curl -s -o /dev/null -D - https://example.com

跟随重定向

-L 跟随 HTTP 3xx 重定向;默认最多跟随 50 跳(使用 --max-redirs 限制)。RFC 301/302 通常将 POST 转为 GET——使用 --post301/--post302 可保留方法。默认情况下,凭据不会发送给重定向的目标主机;--location-trusted 会将其发送到所有主机(存在安全风险)。

curl
# follow 3xx redirects
curl -L https://example.com/old

# limit number of redirects
curl -L --max-redirs 5 https://example.com

# treat redirects as POST (default resends as GET)
curl -L --post301 --post302 https://example.com

# show each hop in a redirect chain
curl -L -v https://short.url/abc 2>&1 | grep -i location

# follow only http/https redirects
curl -L --proto-redir http,https https://example.com

# send credentials only on first request
curl -L --location-trusted -u user:pass https://example.com

URL 语法与通配符

curl 展开 URL 的 'globbing' 模式:[1-10] 范围、{a,b,c} 列表,可带可选的 :step 步长。每次展开是一个独立的请求。使用 -g(--globoff)可将方括号和大括号按字面量处理——这对包含 JSON 数组或 IPv6 地址(如 http://[::1]:8080/)的 URL 至关重要。

curl
# range glob: requests file1, file2, file3
curl -O https://example.com/file[1-3].zip

# range with step
curl -O https://example.com/file[1-10:2].zip   # 1,3,5,7,9

# list glob: a, b, c
curl -O https://example.com/{a,b,c}.zip

# combined globs
curl -O https://example.com/{v1,v2}/file[1-2].txt

# output each URL as it is fetched
curl -O https://example.com/file[1-3].zip --write-out "%{url} -> %{filename}\n"

# disable globbing (literal brackets/braces)
curl -g "https://example.com/file[1].zip"

帮助与版本

--version 显示 curl 版本、支持的协议(HTTP、HTTPS、FTP 等)以及编译时启用的功能(HTTP2、SSL、brotli)。--help all 列出所有选项——当你只记得某个标志的部分名称时非常有用。具体功能取决于 curl 的编译方式(例如 HTTP/3 需要特殊构建)。

curl
# version and supported features
curl --version

# short help (one-line per option)
curl --help

# long help (all options with details)
curl --help all

# search help for a keyword
curl --help all | grep -i cookie

# list supported protocols
curl --version | head -1

# check if a feature is built in
curl --version | grep -i http2
02

HTTP 方法

GET 请求

GET 是默认方法。始终给包含 & 或 ? 的 URL 加引号,以防 shell 后台化或展开它们。-X 显式设置方法,但对 GET 通常不必要。对于 API,设置 Accept 头来协商响应格式。

curl
# simple GET (default method)
curl https://api.example.com/data

# GET with query string
curl "https://api.example.com/search?q=curl&page=2"

# explicit method
curl -X GET https://example.com

# GET with headers
curl -H "Accept: application/json" https://api.example.com/data

# GET with auth
curl -u user:pass https://api.example.com/secret

POST 请求

发送 -d 会自动将方法切换为 POST,因此 -X POST 是冗余的(但无害且更清晰)。要 POST 文件,使用 -d @filename(@ 触发文件读取)。-d 的默认 Content-Type 是 application/x-www-form-urlencoded;用 -H 覆盖以发送 JSON。

curl
# POST with form data (application/x-www-form-urlencoded)
curl -X POST -d "name=Alice&age=30" https://api.example.com/users

# -d implies POST, so -X is optional
curl -d "name=Alice" https://api.example.com/users

# POST with no body
curl -X POST https://api.example.com/trigger

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

# POST with data from a file
curl -X POST -d @form.txt https://api.example.com/users

PUT 与 PATCH

PUT 替换整个资源;PATCH 应用部分更新。两者都需要 -X,因为 curl 没有对应的 -d 快捷方式。-T(upload-file)以流式上传文件作为请求体,非常适合 PUT 上传。发送原始文件时使用 --data-binary 而非 -d,以避免去除换行符。

curl
# PUT (replace entire resource)
curl -X PUT -H "Content-Type: application/json" \
     -d '{"name":"Alice","age":31}' \
     https://api.example.com/users/1

# PATCH (partial update)
curl -X PATCH -H "Content-Type: application/json" \
     -d '{"age":32}' \
     https://api.example.com/users/1

# PUT a file as body
curl -X PUT --data-binary @config.json \
     -H "Content-Type: application/json" \
     https://api.example.com/config

# PUT with upload file
curl -X PUT -T localfile.txt https://example.com/remote.txt

DELETE

DELETE 用于删除资源。大多数 API 要求认证,因此需包含 -u 或 Authorization 头。某些 API 接受请求体(reason、级联标志)——添加 -d 和 Content-Type 头。使用 -w '%{http_code}' 检查结果:204(No Content)和 200 是典型的成功状态。

curl
# delete a resource
curl -X DELETE https://api.example.com/users/1

# delete with auth
curl -X DELETE -H "Authorization: Bearer TOKEN" \
     https://api.example.com/users/1

# delete with confirmation check
curl -X DELETE -w "%{http_code}" -o /dev/null -s \
     https://api.example.com/users/1

# delete with request body (some APIs require it)
curl -X DELETE -H "Content-Type: application/json" \
     -d '{"reason":"spam"}' \
     https://api.example.com/users/1

HEAD 与 OPTIONS

-I 发送 HEAD 请求(仅响应头,无响应体)——适合在不下载的情况下检查大小(Content-Length)、类型(Content-Type)和最后修改时间。OPTIONS 揭示允许的方法(Allow 头),并被浏览器用于 CORS 预检。添加 Origin 和 Access-Control-Request-Method 头可模拟真实的预检请求。

curl
# HEAD: headers only, no body
curl -I https://example.com
curl --head https://example.com

# OPTIONS: discover allowed methods
curl -X OPTIONS https://api.example.com/users

# OPTIONS with CORS preflight headers
curl -X OPTIONS \
     -H "Origin: https://mysite.com" \
     -H "Access-Control-Request-Method: POST" \
     https://api.example.com/users

# check allowed methods from Allow header
curl -X OPTIONS -s -D - -o /dev/null https://api.example.com | grep -i allow
03

请求头

设置请求头 (-H)

-H(--header)添加请求头。多个头使用多个 -H 重复指定。末尾分号(X-Empty;) 发送空值。@headers.txt 从文件读取头(每行一个 'Name: value')——便于将 Authorization 令牌排除在 shell 历史之外。

curl
# single header
curl -H "Accept: application/json" https://api.example.com/data

# multiple headers
curl -H "Accept: application/json" \
     -H "Authorization: Bearer TOKEN" \
     -H "X-Custom: value" \
     https://api.example.com/data

# header with empty value
curl -H "X-Empty;" https://example.com

# headers from a file (one per line)
curl -H @headers.txt https://example.com

# Content-Type for POST
curl -X POST -H "Content-Type: application/json" \
     -d '{"a":1}' https://api.example.com

常见请求头

最常见的请求头:Accept 告诉服务器你想要的格式;Content-Type 描述请求体;Authorization 携带凭据。--compressed 让 curl 请求压缩响应(gzip、br)并自动解压,因此你无需手动处理编码。

curl
# Accept: negotiate response format
curl -H "Accept: application/json" https://api.example.com
curl -H "Accept: text/html" https://example.com

# Content-Type: describe request body
curl -H "Content-Type: application/json" -d '{"a":1}' https://api.example.com
curl -H "Content-Type: application/x-www-form-urlencoded" -d "a=1" https://api.example.com

# Authorization: credentials
curl -H "Authorization: Bearer TOKEN" https://api.example.com

# Accept-Encoding: ask for compression
curl -H "Accept-Encoding: gzip, br" --compressed https://example.com

# Cache-Control
curl -H "Cache-Control: no-cache" https://api.example.com

User-Agent

一些服务器会屏蔽默认的 curl User-Agent。-A(--user-agent)设置它;空 -A 会完全移除它。长格式是 -H 'User-Agent: ...'。许多反爬站点会检查真实浏览器签名,因此通常需要逼真的浏览器 UA。

curl
# default User-Agent looks like: curl/8.7.1
curl -v https://example.com 2>&1 | grep -i user-agent

# set a custom User-Agent
curl -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" https://example.com
curl -H "User-Agent: MyBot/1.0" https://example.com

# empty User-Agent
curl -A "" https://example.com

# fake a real browser (some sites require it)
curl -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) \
AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/120.0 Safari/537.36" https://example.com

# per-site default via config
# user-agent = "MyBot/1.0"  in ~/.curlrc

Referer 与 Host

-e(--referer)设置 Referer 头。覆盖 Host 在通过 IP 测试虚拟主机时至关重要:服务器根据 Host 选择正确的站点。支持 CORS 的端点通常需要 Origin;X-Forwarded-For/Proto 可模拟代理或负载均衡器后面的请求。

curl
# set Referer header
curl -H "Referer: https://google.com" https://example.com/page
curl -e https://google.com https://example.com/page

# set Host header (override DNS)
curl -H "Host: api.example.com" https://192.168.1.10/users

# combined: test a vhost by IP with custom Host
curl -H "Host: api.example.com" \
     -H "X-Forwarded-Proto: https" \
     http://10.0.0.5/health

# Origin header (for CORS/POST forms)
curl -H "Origin: https://mysite.com" \
     -X POST -d "x=1" https://api.example.com/form

# X-Forwarded-For (debugging proxies)
curl -H "X-Forwarded-For: 1.2.3.4" https://api.example.com

移除请求头

使用 -H 'Header-Name:'(带冒号但无值)告诉 curl 不发送该头的值,从而有效移除 User-Agent 或 Accept-Encoding 等默认头。当你指定相同名称时,curl 会替换该头而非发送重复项。这在发送最简请求时很有用。

curl
# remove a header by giving it no value
curl -H "User-Agent:" https://example.com

# remove Accept-Encoding (disable compression)
curl -H "Accept-Encoding:" https://example.com

# remove all auto-added headers and send minimal request
curl -H "Host:" -H "User-Agent:" -H "Accept:" https://example.com

# prevent curl from adding Accept-Encoding
curl --no-keepalive https://example.com

# replace a default header instead of adding a duplicate
# (curl replaces, doesn't duplicate, when -H name matches)
curl -H "Accept: text/plain" https://example.com
04

POST 数据与表单

表单数据 (-d)

-d(--data)发送 URL 编码的表单数据并将方法切换为 POST。多个 -d 标志会用 & 连接。-d 会去除首尾空白和换行符;--data-binary 保留它们;--data-raw 完全禁用 @file 功能(因此以 @ 开头的字面值会原样发送)。

curl
# URL-encoded form data
curl -d "name=Alice&age=30" https://api.example.com/form

# explicit POST (same result; -d implies POST)
curl -X POST -d "name=Alice" https://api.example.com/form

# multiple -d are joined with &
curl -d "name=Alice" -d "age=30" https://api.example.com/form

# -d strips newlines; use --data-binary to preserve them
curl --data-binary "line1\nline2" https://api.example.com

# raw data, no content-type added
curl --data-raw "name=Alice&age=30" https://api.example.com

# default Content-Type: application/x-www-form-urlencoded
curl -v -d "a=1" https://api.example.com 2>&1 | grep -i content-type

URL 编码

--data-urlencode 对值(或整个 name=value)进行百分号编码,因此你无需手动编码空格、&、= 或非 ASCII 字符。使用 name@file 时,文件内容会作为值被 URL 编码。对于用户 supplied 数据始终使用此选项,以避免破坏表单结构。

curl
# spaces and special chars must be encoded
curl -d "name=Alice%20Smith" https://api.example.com
curl -d "name=Alice+Smith" https://api.example.com

# --data-urlencode encodes the value for you
curl --data-urlencode "name=Alice Smith" https://api.example.com
curl --data-urlencode "[email protected]" https://api.example.com

# encode name=value pair (both encoded)
curl --data-urlencode "name=Alice & Bob" https://api.example.com

# encode only the value after =, with explicit content-type
curl --data-urlencode "query=SELECT * FROM users" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     https://api.example.com/search

# encode content of a file
curl --data-urlencode [email protected] https://api.example.com

二进制数据

--data-binary 按字节发送文件(保留换行符和 null 字节),不同于 -d 会去除它们。-T 以流式传输文件,因此大文件上传时内存占用保持很低。要发送预压缩数据,用 -H 'Content-Encoding: gzip' 管道输入——服务器必须支持;否则使用 Transfer-Encoding: chunked。

curl
# send a binary file as the body
curl --data-binary @image.png https://api.example.com/upload

# upload with explicit content type
curl --data-binary @image.png \
     -H "Content-Type: image/png" \
     https://api.example.com/upload

# raw bytes (no @ expansion, no stripping)
curl --data-raw "$(printf '\x00\x01\x02')" https://api.example.com

# stream a large file without buffering in memory
curl -T bigfile.iso https://api.example.com/upload

# send gzipped body
gzip -c data.json | curl --data-binary @- \
     -H "Content-Encoding: gzip" \
     -H "Content-Type: application/json" \
     https://api.example.com/data

从文件读取 (@file)

-d @file 从文件读取请求体(@ 触发文件读取)。--data-binary @file 保留原始字节。-d @- 从 stdin 读取——非常适合管道传输 jq、curl 或其他转换。如果字面值以 @ 开头且你不希望文件展开,使用 --data-raw。

curl
# read body from a file
curl -d @data.txt https://api.example.com/form

# read JSON body from a file
curl -H "Content-Type: application/json" \
     -d @payload.json https://api.example.com

# binary-safe file read
curl --data-binary @image.png https://api.example.com/upload

# read from stdin
echo '{"a":1}' | curl -d @- https://api.example.com

# pipe through curl (transform then send)
jq '{name, age}' input.json | \
  curl -H "Content-Type: application/json" -d @- https://api.example.com

# file name starting with @ (use --data-raw)
curl --data-raw "@mention" https://api.example.com

Content-Type 与表单

始终让 Content-Type 与请求体格式匹配:application/x-www-form-urlencoded(-d 的默认值)、application/json(配合 -H)、multipart/form-data(由 -F 自动设置)、SOAP 用 text/xml。为非 ASCII 数据添加 charset=utf-8 是良好实践。不匹配会导致服务器拒绝或错误解析请求体。

curl
# default for -d: application/x-www-form-urlencoded
curl -d "name=Alice" https://api.example.com

# JSON body
curl -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' https://api.example.com

# multipart form (file upload, see File Upload section)
curl -F "[email protected]" https://api.example.com/upload

# text/plain body
curl -H "Content-Type: text/plain" -d "hello" https://api.example.com

# XML body (SOAP)
curl -H "Content-Type: text/xml" \
     -d '<?xml version="1.0"?><env:Envelope/>' \
     https://api.example.com/soap

# set charset explicitly
curl -H "Content-Type: application/json; charset=utf-8" \
     -d '{"name":"éàç"}' https://api.example.com
05

JSON 请求

基本 JSON POST

JSON 请求需要 -H 'Content-Type: application/json' 加上带 JSON 字符串的 -d。在 JSON 周围使用单引号,这样 shell 不会触碰内部的双引号。通过管道将响应传给 jq '.' 获得可读输出,或 jq '.field' 提取特定值。

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

# -d already implies POST, so -X is optional
curl -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' \
     https://api.example.com/users

# ask for JSON back
curl -H "Accept: application/json" \
     -H "Content-Type: application/json" \
     -d '{"name":"Alice"}' \
     https://api.example.com/users

# pretty-print the JSON response (pipe to jq)
curl -s -H "Accept: application/json" \
     https://api.example.com/users/1 | jq .

从文件发送 JSON

对于复杂 JSON,将其存储在 .json 文件中并使用 -d @file(或 --data-binary @file 以保留包括尾随换行符在内的精确字节)。管道传输 jq -n 让你以编程方式构建 JSON,无需繁琐的转义。始终先用 jq . 验证——语法错误会浪费一次请求。

curl
# POST JSON body from a file
curl -H "Content-Type: application/json" \
     -d @payload.json https://api.example.com/users

# binary-safe (preserves exact bytes)
curl -H "Content-Type: application/json" \
     --data-binary @payload.json https://api.example.com/users

# generate JSON with jq, then POST
jq -n '{name: "Alice", age: 30}' | \
  curl -H "Content-Type: application/json" \
       -d @- https://api.example.com/users

# validate JSON before sending
jq . payload.json && \
  curl -H "Content-Type: application/json" \
       -d @payload.json https://api.example.com/users

# send a JSON array
curl -H "Content-Type: application/json" \
     -d '[{"id":1},{"id":2}]' https://api.example.com/batch

带变量的 JSON

将变量插入 JSON 字符串会在引号和特殊字符上出错。安全的模式是 jq --arg/--argjson:它构建有效的 JSON 并正确转义值。--arg 用于字符串,--argjson 用于数字/布尔值。对于机密信息,优先使用头文件(-H @file)而非命令行参数,以避免通过 ps 泄露。

curl
# interpolate a shell variable into JSON (risky for special chars)
NAME="Alice"
curl -H "Content-Type: application/json" \
     -d "{\"name\":\"$NAME\"}" https://api.example.com

# safer: build JSON with jq
NAME="Alice"
jq -n --arg name "$NAME" '{name: $name}' | \
  curl -H "Content-Type: application/json" -d @- https://api.example.com

# multiple variables
NAME="Alice"; AGE=30
jq -n --arg name "$NAME" --argjson age "$AGE" \
  '{name: $name, age: $age}' | \
  curl -H "Content-Type: application/json" -d @- https://api.example.com

# read secret from env, hide from ps
TOKEN="$API_TOKEN" curl -H "Authorization: Bearer $API_TOKEN" \
     -d '{"x":1}' https://api.example.com

GraphQL 请求

GraphQL 使用单个 POST 端点,请求体是包含 'query' 字符串和可选 'variables' 的 JSON。内联编写查询需要转义内部双引号,因此将复杂查询存储在 .json 文件中并使用 -d @file。内省(__schema 查询)列出 API 的类型,非常适合探索。

curl
# GraphQL query as JSON body
curl -X POST \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer TOKEN" \
     -d '{"query":"{ user(id:1) { name email } }"}' \
     https://api.example.com/graphql

# query with variables
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"query":"query($id:Int!){ user(id:$id){name} }","variables":{"id":1}}' \
     https://api.example.com/graphql

# query from a file (multi-line, no escaping pain)
curl -X POST \
     -H "Content-Type: application/json" \
     -d @query.json https://api.example.com/graphql

# introspection: list all types
curl -s -X POST -H "Content-Type: application/json" \
     -d '{"query":"{ __schema { types { name } } }"}' \
     https://api.example.com/graphql | jq

内容协商

Accept 协商响应格式;许多 REST API 默认返回 JSON,但也支持 XML 或 CSV 替代方案。--compressed 请求 gzip/brotli 并自动解压。API 通常通过 Accept 进行版本控制(例如 GitHub:application/vnd.github+json)。检查服务器实际返回的 Content-Type 以确认协商生效。

curl
# request JSON
curl -H "Accept: application/json" https://api.example.com/users

# request XML
curl -H "Accept: application/xml" https://api.example.com/users

# request a specific version
curl -H "Accept: application/vnd.api+json; version=2" \
     https://api.example.com/users

# request anything (default is */*)
curl -H "Accept: */*" https://example.com

# request compression and auto-decompress
curl --compressed -H "Accept-Encoding: gzip, br" https://example.com

# inspect what the server actually returns
curl -s -o /dev/null -D - https://api.example.com/users | grep -i content-type
06

文件上传

Multipart 上传 (-F)

-F(--form)构建 multipart/form-data 请求——文件上传的标准方式。-F file=@path 读取文件。添加 ;filename= 覆盖服务器看到的文件名,;type= 设置该部分的 Content-Type。同一字段名中的多个文件(许多框架中使用 [])需要多个 -F 标志。

curl
# upload a file as a multipart form field
curl -F "[email protected]" https://api.example.com/upload

# -F sets POST + multipart/form-data automatically
# the @ prefix reads the file

# add a text field alongside the file
curl -F "[email protected]" -F "description=My photo" \
     https://api.example.com/upload

# rename the uploaded file (server sees "avatar.jpg")
curl -F "[email protected];filename=avatar.jpg" \
     https://api.example.com/upload

# set explicit content-type for the file part
curl -F "[email protected];type=image/jpeg" \
     https://api.example.com/upload

# upload multiple files in one field
curl -F "[email protected]" -F "[email protected]" \
     https://api.example.com/upload

多文件与多字段

Multipart 表单可以混合文件和文本字段。在字段名中使用 [] 可实现数组风格上传(Rails/PHP 等框架会将这些解析为数组)。字段可以通过设置 ;type=application/json 携带 JSON——服务器将其作为结构化数据而非文件读取。Curl 原生无法上传目录;先用 tar 打包。

curl
# upload two files and a caption
curl -F "[email protected]" \
     -F "[email protected]" \
     -F "caption=Summer trip" \
     https://api.example.com/gallery

# array-style field names (PHP/Rails style)
curl -F "files[][email protected]" -F "files[][email protected]" \
     https://api.example.com/upload

# send a file's contents as a regular text field
curl -F "[email protected];type=application/json" \
     https://api.example.com/upload

# mix file and JSON fields
curl -F "[email protected]" \
     -F "options={\"priority\":\"high\"};type=application/json" \
     https://api.example.com/upload

# upload a directory as a tar (workaround)
tar -cf - mydir/ | curl -F "tar=@-;filename=dir.tar" \
     https://api.example.com/upload

流式上传 (-T)

-T(--upload-file)使用 PUT 并以流式传输文件,因此即使是巨大的上传,内存占用也保持平稳——远优于 -d @file 处理大数据。-T - 从 stdin 读取。-C - 恢复中断的传输。URL 末尾带斜杠时,curl 会附加本地文件名(类似 HTTP PUT 的镜像行为)。

curl
# upload a file with PUT (streaming, low memory)
curl -T localfile.txt https://example.com/upload/remote.txt

# upload to a URL with the local filename
curl -T localfile.txt https://example.com/upload/

# stream from stdin (pipe into PUT body)
cat data.json | curl -T - https://api.example.com/data

# upload with authentication
curl -T file.zip -u user:pass \
     https://example.com/upload/file.zip

# upload with progress bar
curl -T bigfile.iso --progress-bar \
     https://example.com/upload/bigfile.iso

# resume an interrupted upload
curl -T bigfile.iso -C - https://example.com/upload/bigfile.iso

带进度的上传

默认情况下,curl 在 stderr 显示进度条。--progress-bar 切换为更简洁的进度条。-s(静默)隐藏它;添加 S(-sS)仍显示错误。进度信息输出到 stderr,因此 stdout(响应体)保持干净——管道传输时很有用。使用 -w 配合 %{size_upload} 和 %{time_total} 获取一行摘要。

curl
# default progress meter (terminal)
curl -T file.zip https://example.com/upload

# simple progress bar
curl -T file.zip --progress-bar https://example.com/upload

# no progress at all (silent)
curl -T file.zip -s https://example.com/upload

# show progress but only errors (silent + show-error)
curl -T file.zip -sS https://example.com/upload

# custom progress with --write-out
curl -T file.zip -w "Uploaded %{size_upload} bytes in %{time_total}s\n" \
     -o /dev/null https://example.com/upload

# redirect progress to stderr, body to file
curl -T file.zip --progress-bar 2> progress.log \
     -o response.txt https://example.com/upload

上传字段与类型

Curl 根据文件扩展名猜测 MIME 类型。用 ;type= 覆盖。;filename= 选项更改服务器看到的名称,便于使用净化后的名称。< 前缀将文件内容读入文本字段(而非作为文件发送)——当 API 期望字符串但你的数据在文件中时很有用。

curl
# specify MIME type explicitly
curl -F "[email protected];type=text/csv" https://api.example.com/import

# let curl guess from the extension (default)
curl -F "[email protected]" https://api.example.com/upload

# force generic type
curl -F "[email protected];type=application/octet-stream" \
     https://api.example.com/upload

# send a file with a custom filename visible to server
curl -F "upload=@/tmp/abc123;filename=report.pdf;type=application/pdf" \
     https://api.example.com/upload

# send a string as if it were a file
curl -F "file=hello world" https://api.example.com/upload

# send a string as a file with a filename
curl -F "file=<inline.txt" https://api.example.com/upload
07

认证

Basic 认证 (-u)

-u(--user)发送 HTTP Basic 认证(base64 编码的 user:pass)。省略密码会触发交互式提示(更安全——避免出现在 shell 历史中)。--netrc 从 ~/.netrc 读取凭据,因此它们永远不会出现在命令行上。Basic 在明文 HTTP 上不安全;始终使用 HTTPS。任何有 shell 访问权限的人都可通过 ps 看到 -u 参数。

curl
# HTTP Basic authentication
curl -u user:pass https://api.example.com/secret
curl --user user:pass https://api.example.com/secret

# prompt for password (don't put it on the command line)
curl -u user https://api.example.com/secret

# basic auth with explicit method (curl picks the safest by default)
curl -u user:pass --basic https://api.example.com/secret

# use a .netrc file instead of -u
echo "machine example.com login user password pass" > ~/.netrc
curl --netrc https://example.com/secret

# base64-encode credentials manually (equivalent)
curl -H "Authorization: Basic $(echo -n user:pass | base64)" \
     https://api.example.com/secret

Bearer 令牌

Bearer 令牌(OAuth2、JWT)放在 Authorization 头中。避免将令牌放在命令行上——ps 会将其暴露给其他用户。从文件(-H @file)或环境变量读取,对于敏感值使用头文件。refresh-token 片段展示了标准的 OAuth2 客户端凭据流程。

curl
# Bearer token (OAuth2 / JWT)
curl -H "Authorization: Bearer ACCESS_TOKEN" \
     https://api.example.com/protected

# token from an environment variable
curl -H "Authorization: Bearer $TOKEN" \
     https://api.example.com/protected

# token from a file (avoids shell history and ps)
curl -H "Authorization: Bearer $(cat ~/.token)" \
     https://api.example.com/protected

# headers from a file (cleanest for secrets)
echo "Authorization: Bearer $TOKEN" > /tmp/hdr.txt
curl -H @/tmp/hdr.txt https://api.example.com/protected
rm /tmp/hdr.txt

# refresh token flow (simplified)
TOKEN=$(curl -s -u client:secret -d "grant_type=refresh_token" \
        -d "refresh_token=$REFRESH" \
        https://auth.example.com/token | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" https://api.example.com/data

API 密钥(Header)

API 密钥出现在自定义头(X-API-Key、X-Client-Id)或作为查询参数。查询字符串会泄露到服务器日志和浏览器历史中,因此优先使用头。对于 AWS,--aws-sigv4 用你的 access/secret 密钥签署请求。GitHub 使用 Bearer 加个人访问令牌(ghp_...)。

curl
# API key in a custom header (varies by service)
curl -H "X-API-Key: abc123" https://api.example.com/data

# API key as a query parameter
curl "https://api.example.com/data?api_key=abc123"

# GitHub personal access token
curl -H "Authorization: Bearer ghp_TOKEN" \
     https://api.github.com/user

# AWS-style signature v4 (use the AWS CLI or aws-sigv4)
curl --aws-sigv4 "aws:amz:us-east-1:service" \
     -u "ACCESS:SECRET" https://service.amazonaws.com/

# multiple keys
curl -H "X-API-Key: abc123" \
     -H "X-Client-Id: my-app" \
     https://api.example.com/data

Netrc 文件

--netrc 从 ~/.netrc 读取凭据,将主机映射到登录名/密码对。文件必须 chmod 600,否则 curl 拒绝使用。--netrc-file 指向自定义位置。'default' 条目适用于任何没有特定 machine 条目的主机。这是让机密信息远离命令行的最干净方式。

curl
# ~/.netrc stores credentials per host
# format:
#   machine api.example.com
#   login alice
#   password secret
#   machine github.com
#   login alice
#   password ghp_TOKEN

# use netrc automatically
curl --netrc https://api.example.com/data

# use a custom netrc file
curl --netrc-file ~/.netrc.work https://api.example.com/data

# ignore netrc for this request
curl --netrc-optional -u user:pass https://api.example.com/data

# secure the file (required by curl on some systems)
chmod 600 ~/.netrc

# machine + default for any host
# default
# login anonymous
# password guest

Digest 与 NTLM

Basic 以明文发送凭据(在 HTTPS 下没问题)。Digest 发送哈希,从不发送密码。NTLM 和 Negotiate 处理 Windows/Kerberos SSO。--anyauth 让 curl 根据服务器的 WWW-Authenticate 头选择方法。服务器必须支持该方法;-v 显示它发出的挑战。

curl
# digest authentication (challenge-response, more secure than Basic)
curl -u user:pass --digest https://api.example.com/secret

# NTLM (Windows auth)
curl -u user:pass --ntlm https://intranet.example.com/

# negotiate (Kerberos/SPNEGO)
curl -u user:pass --negotiate https://intranet.example.com/

# anyauth: let curl pick the method
curl -u user:pass --anyauth https://api.example.com/secret

# specify the service principal for negotiate
curl -u user:pass --negotiate --service-name HTTP/intranet.example.com \
     https://intranet.example.com/

# show which method the server requested
curl -v -u user:pass --anyauth https://api.example.com 2>&1 | grep -i www-authenticate

OAuth 与令牌

OAuth2 有多种授权类型:client_credentials(服务器到服务器)、authorization_code(浏览器登录后)、password(旧版)。令牌端点返回 access_token(通常还有 refresh_token);将 access_token 作为 Bearer 头用于后续请求。应用密码(GitLab、GitHub)可与 -u 配合使用,绕过 2FA 限制。

curl
# OAuth2 client credentials grant
curl -u client_id:client_secret \
     -d "grant_type=client_credentials" \
     -d "scope=read write" \
     https://auth.example.com/oauth/token

# OAuth2 authorization code (after browser redirect)
curl -d "grant_type=authorization_code" \
     -d "code=AUTH_CODE" \
     -d "redirect_uri=https://app.example.com/callback" \
     -u client_id:client_secret \
     https://auth.example.com/oauth/token

# OAuth2 password grant (deprecated but still seen)
curl -d "grant_type=password" \
     -d "username=user" -d "password=pass" \
     -u client_id:client_secret \
     https://auth.example.com/oauth/token

# use the access token
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
     https://api.example.com/me

# basic auth with username and app password (e.g., GitLab)
curl -u "user:app_password" https://gitlab.example.com/api/v4/user
08

Cookie

发送 Cookie (-b)

-b(--cookie)发送 Cookie。字符串原样发送;文件名(Netscape 格式)加载已保存的 Cookie。多个 Cookie 用分号分隔。-b 也设置 Cookie 头——等价于 -H 'Cookie: ...'。Curl 默认不解析 Set-Cookie;将 -b 与 -c 配合以持久化新 Cookie。

curl
# send a single cookie
curl -b "session=abc123" https://example.com/dashboard

# send multiple cookies
curl -b "session=abc123; theme=dark; lang=en" \
     https://example.com/dashboard

# send cookies from a file (Netscape format)
curl -b cookies.txt https://example.com/dashboard

# send a cookie with attributes (path, domain)
curl -b "session=abc123; Path=/; Domain=.example.com" \
     https://example.com/

# cookie via header (manual)
curl -H "Cookie: session=abc123; theme=dark" \
     https://example.com/dashboard

保存 Cookie (-c)

-c(--cookie-jar)将 Set-Cookie 响应头以 Netscape 格式写入文件。将 -b(读)与 -c(写)使用同一文件可在多次请求间维持会话:登录一次,然后为已认证的调用重用 cookie jar。-c - 转储到 stdout 以供检查。

curl
# save response cookies to a file
curl -c cookies.txt https://example.com/login

# send and save cookies in one request
curl -b cookies.txt -c cookies.txt https://example.com/dashboard

# dump cookies to stdout (Netscape format)
curl -c - https://example.com/

# inspect cookies with --dump-header
curl -D headers.txt https://example.com/
grep -i set-cookie headers.txt

# login then reuse the session cookie
curl -c cookies.txt -d "user=alice&pass=secret" \
     https://example.com/login
curl -b cookies.txt https://example.com/dashboard

Cookie Jar

Cookie jar(Netscape 格式)在 curl 调用之间持久化 Cookie,模拟浏览器会话。对 -b 和 -c 使用同一 jar 文件,以便 Cookie 在每次请求时刷新。格式是制表符分隔的:domain、include-subdomain 标志、path、secure 标志、过期时间(unix 时间戳)、name、value。删除 jar 即可登出。

curl
# Netscape cookie file format:
# domain  flag  path  secure  expiration  name  value
# .example.com  TRUE  /  FALSE  1735689600  session  abc123

# full session: login, then call API with the same jar
JAR=/tmp/cookies.txt
curl -s -c "$JAR" -d "user=alice&pass=secret" https://example.com/login
curl -s -b "$JAR" https://example.com/api/profile
curl -s -b "$JAR" https://example.com/api/settings

# update the jar on every request (refresh expiring cookies)
curl -b "$JAR" -c "$JAR" https://example.com/api/data

# expire all cookies by deleting the jar
rm "$JAR"

# view the jar contents
cat "$JAR"
# or human-readable
curl -b "$JAR" -c - -o /dev/null https://example.com/

从字符串创建 Cookie

对于一次性请求,使用带字符串的 -b 比 jar 更简单。Cookie 用分号分隔;带特殊字符的值应进行 URL 编码。Cookie 头(通过 -H)是等价的,可让你原样粘贴浏览器的 Cookie 字符串——便于重放捕获的会话。

curl
# one-off cookie (no file)
curl -b "session=abc123" https://example.com/

# multiple cookies in one string
curl -b "session=abc123; theme=dark; lang=en" https://example.com/

# build cookie string from variables
SID="abc123"; THEME="dark"
curl -b "session=$SID; theme=$THEME" https://example.com/

# encode a value containing special chars
curl -b "data=$(python3 -c 'import urllib.parse;print(urllib.parse.quote("a&b"))')" \
     https://example.com/

# emulate a browser cookie header exactly
curl -H "Cookie: __cfduid=abc; _ga=GA1.2.xxx; session=xyz" \
     https://example.com/

会话管理

Web 登录通常需要登录页面的 CSRF 令牌加 Cookie 持久化。模式:GET 表单(保存 Cookie),提取令牌,POST 凭据(带 Cookie + 令牌),然后为已认证的调用重用 jar。每次请求更新 jar(-c)以便刷新的会话 Cookie 传播。完成后始终登出并删除 jar。

curl
# full login flow with cookie persistence
JAR=/tmp/session.txt

# 1. GET login page (capture CSRF token + cookies)
curl -s -c "$JAR" https://example.com/login > login.html
CSRF=$(grep csrf_token login.html | head -1 | grep -oP 'value="\K[^"]+')

# 2. POST credentials with CSRF and cookies
curl -s -b "$JAR" -c "$JAR" \
     -d "user=alice&pass=secret&csrf=$CSRF" \
     https://example.com/login

# 3. Access protected resources with the session
curl -s -b "$JAR" https://example.com/dashboard
curl -s -b "$JAR" https://example.com/api/data

# 4. Logout (invalidate session)
curl -s -b "$JAR" -c "$JAR" https://example.com/logout
rm "$JAR"
09

代理

HTTP/HTTPS 代理

-x(--proxy)通过代理路由请求。http_proxy/https_proxy/no_proxy 环境变量会被自动遵循。对于通过 HTTP 代理访问 HTTPS 目标,curl 使用 CONNECT 建立隧道。设置 no_proxy 可绕过内部主机的代理——逗号分隔,前导点表示子域。

curl
# use a proxy for all requests
curl -x http://proxy.example.com:8080 https://example.com
curl --proxy http://proxy.example.com:8080 https://example.com

# HTTPS proxy (proxy connection itself is TLS)
curl -x https://proxy.example.com:8443 https://example.com

# proxy via environment variable
export https_proxy=http://proxy.example.com:8080
curl https://example.com

# proxy for a specific scheme only
curl --proxy http://proxy:8080 http://example.com
curl --proxy https://secure-proxy:8443 https://example.com

# bypass proxy for specific hosts
export no_proxy="localhost,127.0.0.1,.internal.example.com"
curl https://internal.example.com   # direct, no proxy

SOCKS 代理

SOCKS5 与协议无关,因此可隧道传输 HTTP、HTTPS 等。--socks5-hostname 让代理解析 DNS(适用于你的机器无法解析的主机)。一个常见技巧:ssh -D 1080 创建本地 SOCKS 代理,通过远程 SSH 服务器隧道传输流量——适合在不受信任的网络上安全浏览。

curl
# SOCKS5 proxy
curl -x socks5://proxy.example.com:1080 https://example.com
curl --socks5 proxy.example.com:1080 https://example.com

# SOCKS5 with local DNS resolution (server resolves)
curl --socks5-hostname proxy.example.com:1080 https://example.com

# SOCKS4 proxy
curl --socks4 proxy.example.com:1080 https://example.com

# SSH tunnel as a SOCKS proxy (run separately)
# ssh -D 1080 user@remote -N
curl -x socks5://localhost:1080 https://example.com

# SOCKS proxy with authentication
curl -x socks5://user:[email protected]:1080 https://example.com

代理认证

代理通常需要认证。-U(--proxy-user)提供凭据;--proxy-ntlm/--proxy-digest/--proxy-anyauth 选择方案。在 -x URL 中嵌入 user:pass 是等价的。对于 NTLM(企业 Windows 代理),--proxy-ntlm 处理多步握手。-U user(无密码)会交互式提示。

curl
# proxy with username/password
curl -x http://user:[email protected]:8080 https://example.com

# separate proxy auth flag
curl -x http://proxy.example.com:8080 -U user:pass https://example.com
curl --proxy-user user:pass -x http://proxy.example.com:8080 https://example.com

# prompt for proxy password
curl -U user -x http://proxy.example.com:8080 https://example.com

# NTLM-authenticated proxy
curl -U user:pass --proxy-ntlm \
     -x http://proxy.example.com:8080 https://example.com

# digest-authenticated proxy
curl -U user:pass --proxy-digest \
     -x http://proxy.example.com:8080 https://example.com

# anyauth for proxy
curl -U user:pass --proxy-anyauth \
     -x http://proxy.example.com:8080 https://example.com

No Proxy 与绕过

--noproxy(或 no_proxy 环境变量)列出绕过代理的主机。前导点表示'此域及其所有子域'。'*' 对所有主机绕过代理。CIDR 范围在较新的 curl 中可用。当外部代理无法访问内部企业主机时,这至关重要。

curl
# bypass proxy for specific hosts
curl --noproxy "localhost,127.0.0.1,.internal.com" \
     -x http://proxy:8080 https://example.com

# via environment variable
export no_proxy="localhost,127.0.0.1,.internal.com,10.0.0.0/8"
curl https://internal.com   # direct

# wildcard match for a domain and all subdomains
export no_proxy=".example.com"
curl https://api.example.com   # bypasses proxy

# disable proxy entirely for one request
curl --noproxy "*" -x http://proxy:8080 https://example.com

# ignore *_proxy env vars for one request
curl --noproxy "*" https://example.com

# CIDR ranges (curl 7.86+)
export no_proxy="192.168.0.0/16,10.0.0.0/8"

代理头与隧道

--proxy-header 添加专供代理(不转发到目标)的头,如 Proxy-Authorization。-p(--proxytunnel)强制 curl 即使对明文 HTTP 也使用 CONNECT(通常 CONNECT 仅用于 HTTPS)。当代理拦截明文 HTTP 而你想要端到端隧道时,这很有用。

curl
# add a header sent only to the proxy (not the target)
curl -x http://proxy:8080 \
     --proxy-header "Proxy-Authorization: Bearer xyz" \
     https://example.com

# custom header to proxy + normal header to target
curl -x http://proxy:8080 \
     --proxy-header "X-Proxy-Tag: abc" \
     -H "X-Target-Tag: def" \
     https://example.com

# force CONNECT tunneling even for HTTP
curl -p -x http://proxy:8080 http://example.com
curl --proxytunnel -x http://proxy:8080 http://example.com

# inspect the CONNECT request to the proxy
curl -v -x http://proxy:8080 https://example.com 2>&1 | grep -i connect

# disable proxy tunneling (send HTTP directly through proxy)
curl --proxytunnel -p -x http://proxy:8080 http://example.com
10

SSL/TLS

跳过证书验证 (--insecure)

-k(--insecure)跳过证书验证——仅用于自签名的开发证书,绝不要在生产中使用,因为它允许中间人攻击。默认验证证书链和主机名。--resolve 让你在保留正确 Host/SNI 的同时固定一个 IP——适合测试负载均衡器后面的特定后端。

curl
# skip certificate verification (DANGEROUS)
curl -k https://self-signed.example.com
curl --insecure https://self-signed.example.com

# verify normally (default)
curl https://example.com

# show the cert chain
curl -v https://example.com 2>&1 | grep -E "SSL|cert"

# only verify the cert, not the hostname
curl --insecure https://example.com   # both off
# (no built-in flag to verify cert but skip hostname;
#  use a custom CA bundle instead)

# connect to a host with a mismatched certificate
curl -k --resolve example.com:443:1.2.3.4 https://example.com

# debug TLS handshake
curl -v https://example.com 2>&1 | grep -E "TLS|SSL|cert|cipher"

客户端证书

双向 TLS(mTLS)需要客户端证书(--cert)及其私钥(--key)。PEM 是默认格式;--cert-type P12 处理 PKCS#12 包。--pass 提供密钥密码。--cacert 覆盖受信任的 CA 列表(默认:系统证书库)——适用于内部 CA。mTLS 在零信任和服务网格设置中很常见。

curl
# client certificate + private key (mTLS)
curl --cert client.pem --key client.key \
     https://mtls.example.com/api

# PKCS#12 bundle (combined cert + key)
curl --cert client.p12 --cert-type P12 \
     https://mtls.example.com/api

# password-protected key
curl --cert client.pem --key client.key \
     --pass secret https://mtls.example.com/api

# PEM cert with password
curl --cert client.pem:secret https://mtls.example.com/api

# specify CA bundle to verify the server
curl --cacert custom-ca.pem https://internal.example.com

# mutual TLS with custom CA
curl --cert client.pem --key client.key \
     --cacert internal-ca.pem \
     https://mtls.internal.example.com

TLS 版本

--tlsv1.X 设置最低版本;--tls-max 设置上限。强制 1.2+ 可禁用有已知弱点的旧协议(SSLv3、TLS 1.0/1.1)。默认协商双方都支持的最高版本。-v 在 'SSL connection using' 行中揭示协商的版本和密码套件。

curl
# force a minimum TLS version
curl --tlsv1.2 https://example.com
curl --tlsv1.3 https://example.com

# force an exact TLS version
curl --tls-max 1.2 https://example.com   # cap at 1.2
curl --tls-max 1.3 https://example.com

# negotiate the highest version both support (default)
curl https://example.com

# disable specific versions
curl --tlsv1.2 --tls-max 1.2 https://example.com   # only 1.2

# list versions curl supports
curl --version | grep -i tls

# debug which version was negotiated
curl -v https://example.com 2>&1 | grep -i "SSL connection"

密码套件与 CA 包

--ciphers 限制密码套件(OpenSSL 语法)。--cacert 指向自定义 CA 包,用于验证使用内部/私有 CA 的服务器——默认使用系统证书包(/etc/ssl/certs 或 macOS 钥匙串)。如果 CA 包过期,有效证书的服务器可能被拒绝;更新 curl(或 ca-certificates 包)可修复此问题。

curl
# specify allowed ciphers
curl --ciphers "AES256-GCM-SHA384:AES128-GCM-SHA256" \
     https://example.com

# use a custom CA bundle to verify the server
curl --cacert /etc/ssl/certs/internal-ca.pem \
     https://internal.example.com

# use the system CA store (default)
curl https://example.com

# use Mozilla's CA bundle (download separately)
curl --cacert cacert.pem https://example.com

# show the cipher used
curl -v https://example.com 2>&1 | grep -i cipher

# disable certificate revocation checks (CRL/OCSP)
curl --cacert ca.pem --no-clobber https://example.com

证书检查

使用 -v 检查服务器证书(subject、issuer、有效期)。--pinnedpubkey 强制证书固定——如果服务器的公钥不匹配,请求失败,从而击败恶意 CA。对于深度 TLS 调试,--trace-ascii 转储整个握手过程。openssl s_client 是证书检查的更强大替代方案。

curl
# show the server certificate in verbose mode
curl -v https://example.com 2>&1 | grep -A1 "server certificate"

# extract certificate details (subject, issuer, dates)
curl -v https://example.com 2>&1 | grep -E "subject|issuer|start date|expire date"

# download the server certificate (PEM)
curl -v https://example.com 2>&1 | \
  awk '/-----BEGIN CERT/,/-----END CERT/' > server.pem

# verify cert only, don't transfer body
curl -I https://example.com

# pin a specific certificate (fingerprint)
curl --pinnedpubkey sha256//BASE64= https://example.com

# check OCSP stapling
curl -v https://example.com 2>&1 | grep -i ocsp

# show full TLS trace
curl --trace-ascii - https://example.com | grep -i cert
11

下载

保存到文件 (-o/-O)

-o 写入自定义文件名;-O 使用远程文件名(URL 的 basename)。--output-dir 为 -O 设置目标文件夹。多个 -O 标志在一次调用中下载多个文件。-o /dev/null 丢弃响应体——适用于触发 webhook 或测量时序而不保存数据。

curl
# save with a custom name
curl -o page.html https://example.com/index.html

# save with the remote filename
curl -O https://example.com/file.zip

# save to a specific directory
curl -O --output-dir /tmp/downloads https://example.com/file.zip

# save with remote name to a specific dir
curl -o /tmp/file.zip https://example.com/file.zip

# download multiple files with remote names
curl -O https://example.com/a.zip -O https://example.com/b.zip

# save to /dev/null (just trigger the request)
curl -o /dev/null https://example.com/webhook

断点续传 (-C)

-C -(--continue-at -)使用现有文件大小作为字节偏移量,从上次中断处恢复下载。服务器必须支持 Range 请求(检查 Accept-Ranges: bytes)。与 --retry 配合处理不稳定连接——curl 重试并恢复,避免浪费带宽。

curl
# resume a partial download
curl -C - -o file.zip https://example.com/file.zip
curl --continue-at - -o file.zip https://example.com/file.zip

# resume with -O (remote name)
curl -C - -O https://example.com/file.zip

# start download at a specific byte offset
curl -C 1024 -o file.zip https://example.com/file.zip

# server must support Range requests
curl -I https://example.com/file.zip | grep -i accept-ranges

# resume an interrupted large download
curl -C - --progress-bar -O https://example.com/bigfile.iso

# combine with retry for unreliable connections
curl -C - --retry 5 --retry-all-errors -O \
     https://example.com/bigfile.iso

进度条

--progress-bar 显示更简洁的 ### 条而非默认的进度计。-s 静默所有进度;-sS 保留错误。进度输出到 stderr,因此不会污染 stdout 上的响应体——你可以分别捕获两者。使用 -w 配合 %{size_download} 和 %{time_total} 为脚本提供干净的一行摘要。

curl
# default progress meter
curl -O https://example.com/file.zip

# simple progress bar (alternative style)
curl --progress-bar -O https://example.com/file.zip

# silent (no progress, no body output)
curl -s -O https://example.com/file.zip

# silent but still show errors
curl -sS -O https://example.com/file.zip

# show only errors (no progress, body to file)
curl -sS -o file.zip https://example.com/file.zip

# custom one-line progress
curl -o file.zip -w "%{size_download} bytes in %{time_total}s\n" \
     https://example.com/file.zip

# redirect progress to a file, body to another
curl -o file.zip --progress-bar 2> progress.log \
     https://example.com/file.zip

范围下载

-r(--range)请求字节范围,返回 206 Partial Content。用例:预览大文件、并行多连接下载(拆分为范围、并发获取、拼接)和恢复中断的传输。服务器必须支持 Accept-Ranges: bytes。这是 aria2 等下载加速器的基础。

curl
# download bytes 0-1023 (first 1KB)
curl -r 0-1023 -o part1.bin https://example.com/file.zip

# download from byte 1024 to end
curl -r 1024- -o rest.bin https://example.com/file.zip

# download last 500 bytes
curl -r -500 -o tail.bin https://example.com/file.zip

# check if server supports ranges
curl -I https://example.com/file.zip | grep -i accept-ranges
# Accept-Ranges: bytes  -> supported

# parallel range download (split a large file)
curl -r 0-49999999    -o part1.bin https://example.com/big.iso &
curl -r 50000000-99999999 -o part2.bin https://example.com/big.iso &
wait
cat part1.bin part2.bin > big.iso

# HTTP/1.1 206 Partial Content confirms success
curl -r 0-100 -v https://example.com/file.zip 2>&1 | grep HTTP

多文件与通配符

URL 通配符([1-3]、{a,b,c})每次展开发起一个请求。--output-dir 设置 -O 的保存位置。要实现真正的并行(curl 本身是顺序的),将 URL 管道传输给 xargs -P N 同时运行 N 个 curl。这能显著加快从同一主机下载许多小文件的速度(请尊重服务器的速率限制)。

curl
# download file1.zip, file2.zip, file3.zip
curl -O https://example.com/file[1-3].zip

# download with a step (1, 3, 5, 7, 9)
curl -O https://example.com/file[1-9:2].zip

# download from multiple paths
curl -O https://example.com/{a,b,c}.zip

# download to a specific directory
curl -O --output-dir /tmp/downloads \
     https://example.com/file[1-3].zip

# sequential download (one at a time)
for url in https://example.com/{a,b,c}.zip; do
  curl -O "$url"
done

# parallel download with xargs
echo "https://example.com/{a,b,c}.zip" | tr ' ' '\n' | \
  xargs -n1 -P3 curl -O

镜像与递归

Curl 是单 URL 的——它不递归。要镜像站点,使用 wget --mirror 或用 grep 脚本化链接提取。对于批量下载,将 URL 列在文件中并管道给 xargs -P N 实现并行。sitemap 示例获取并下载站点索引的每个页面——适合离线快照。

curl
# curl doesn't recurse like wget, but you can script it
# download a page and extract its links
curl -s https://example.com/ | \
  grep -oP 'href="\K[^"]+' | grep '^http' > urls.txt

# download all linked PDFs
curl -s https://example.com/docs | \
  grep -oP 'href="\K[^"]+\.pdf' | \
  while read pdf; do curl -O "https://example.com/$pdf"; done

# use wget instead for true mirroring
# wget --mirror --convert-links --page-requisites https://example.com/

# download a list of URLs from a file
xargs -n1 -P4 curl -O < urls.txt

# fetch a sitemap and download every URL
curl -s https://example.com/sitemap.xml | \
  grep -oP '<loc>\K[^<]+' | xargs -n1 -P4 curl -O
12

超时与重试

连接超时

--connect-timeout 限制建立 TCP/TLS 连接的时间(不包括传输)。没有它,curl 可能在不可达主机上挂起数分钟(操作系统默认约 2 分钟)。在脚本中始终同时设置 --connect-timeout(对死主机快速失败)和 --max-time(限制整个操作)以避免挂起。

curl
# max time to establish the connection (seconds)
curl --connect-timeout 10 https://example.com

# connect timeout in milliseconds (curl 7.32+)
curl --connect-timeout 2.5 https://example.com

# connect timeout + overall max time
curl --connect-timeout 10 --max-time 30 https://example.com

# DNS resolution timeout (curl 7.88+, otherwise OS-controlled)
curl --happy-eyeballs-timeout-ms 1000 https://example.com

# fail fast on unreachable hosts
curl --connect-timeout 3 -sS https://example.com || echo "unreachable"

# combine with --retry for resilience
curl --connect-timeout 5 --max-time 20 --retry 3 \
     https://example.com

最大时长

--max-time(-m)限制整个操作(DNS + 连接 + 传输)。--speed-time/--speed-limit 在传输速率低于限制达指定时长时中止——非常适合停滞的下载。退出码 28 表示超时;在脚本中检查 $?。始终设置 max-time 以防止在缓慢或卡住的服务器上无限挂起。

curl
# overall timeout for the whole operation
curl --max-time 30 https://example.com
curl -m 30 https://example.com

# max time in milliseconds (decimal)
curl --max-time 2.5 https://example.com

# max time with no data transfer (idle timeout)
curl --speed-time 30 --speed-limit 1000 https://example.com
# abort if speed < 1000 bytes/sec for 30 sec

# max time for a file upload
curl -T big.iso --max-time 600 https://example.com/upload

# max time per redirect hop
curl -L --max-time 60 https://example.com/redirect

# check exit code (28 = timed out)
curl --max-time 5 https://slow.example.com || echo "exit code: $?"

重试

--retry 默认在瞬时错误(超时、5xx、429)时重试;--retry-all-errors 扩展到所有失败。--retry-delay 在重试之间添加固定等待(每次翻倍)。--retry-connrefused 即使连接被拒绝也重试(例如服务器重启中)。与 -C - 配合以恢复部分下载。这对健壮的脚本至关重要。

curl
# retry up to 3 times on transient errors
curl --retry 3 https://example.com

# retry on any error (not just transient)
curl --retry 3 --retry-all-errors https://example.com

# retry with exponential backoff
curl --retry 5 --retry-delay 2 https://example.com
# waits 2s, 4s, 6s, 8s, 10s between retries (curl 7.66+)

# retry on connection-refused specifically
curl --retry 3 --retry-connrefused https://example.com

# retry only on HTTP 5xx and 429
curl --retry 3 --retry-all-errors \
     -H "Accept: application/json" https://api.example.com

# combine with resume for large downloads
curl --retry 5 --retry-all-errors -C - -O \
     https://example.com/bigfile.iso

重试延迟与退避

--retry-delay N 在第一次重试前等待 N 秒,然后翻倍(N、2N、4N...)。curl 遵循服务器的 Retry-After 头处理 429/503 响应。对于自定义逻辑(抖动、总等待上限、状态码条件),用 shell 循环包装 curl。始终将 --retry 与 --max-time 配合,以免重试永远进行。

curl
# fixed delay between retries
curl --retry 5 --retry-delay 2 https://example.com

# exponential backoff (curl doubles the delay each time)
curl --retry 5 --retry-delay 1 https://example.com
# waits: 1s, 2s, 4s, 8s, 16s

# honor Retry-After header (default with --retry)
curl --retry 3 https://example.com

# cap retry delay with a max-time
curl --retry 10 --retry-delay 1 --max-time 60 https://example.com

# custom backoff in a shell loop
for i in 1 2 3 4 5; do
  curl -s --max-time 10 https://example.com && break
  echo "attempt $i failed, waiting $((i*2))s..."
  sleep $((i*2))
done

# retry on specific HTTP status codes
curl --retry 5 --retry-all-errors \
     -w "%{http_code}" -o /dev/null -s https://example.com

速度限制

--speed-time/--speed-limit 在 N 秒内速率低于限制时中止停滞的传输——退出码 28。--limit-rate 限制传输速率(支持 K/M/G 后缀),适用于不饱和带宽或测试慢速网络。与 --retry 配合以自动恢复停滞的下载。

curl
# abort if speed drops below 1000 bytes/sec for 30 sec
curl --speed-limit 1000 --speed-time 30 -O \
     https://example.com/bigfile.iso

# shorthand: --speed-time defaults to 30 if only --speed-limit is set
curl --speed-limit 1000 -O https://example.com/bigfile.iso

# limit max download rate (bytes/sec)
curl --limit-rate 100k -O https://example.com/file.zip

# limit rate with units (K, M, G)
curl --limit-rate 1M -O https://example.com/file.zip

# limit upload rate
curl -T big.iso --limit-rate 500k https://example.com/upload

# combine with retry on stall
curl --speed-limit 1000 --speed-time 30 \
     --retry 5 --retry-all-errors \
     -O https://example.com/bigfile.iso
13

调试

详细模式 (-v)

-v(verbose)是主要的调试工具。'>' 行是请求,'<' 行是响应,'*' 行是 curl 内部信息(DNS、TLS、连接)——全部在 stderr 上,因此 stdout 保持干净的响应体。重定向 2>&1 以用 grep 过滤。使用 -o /dev/null 检查响应头而不保存响应体。

curl
# verbose: full request + response + curl internals
curl -v https://example.com

# output goes to stderr; body to stdout
curl -v https://example.com > body.txt 2> debug.log

# only show request headers (> lines)
curl -v https://example.com 2>&1 | grep '^>'

# only show response headers (< lines)
curl -v https://example.com 2>&1 | grep '^<'

# only show curl info (* lines)
curl -v https://example.com 2>&1 | grep '^\*'

# verbose without sending the body
curl -v -o /dev/null https://example.com

Trace

--trace 转储所有内容(包括响应体)为十六进制 + ASCII;--trace-ascii 跳过十六进制。--trace-time 添加时间戳。与 -v 不同,trace 显示请求和响应体,因此它是调试二进制或压缩数据的首选。警告:--trace 会记录机密信息(密码、令牌)——分享前请清除。

curl
# trace: dump all sent and received data (hex + ASCII)
curl --trace - https://example.com

# trace ASCII only (no hex dump)
curl --trace-ascii - https://example.com

# trace to a file
curl --trace trace.log https://example.com

# trace including time per packet
curl --trace-ascii - --trace-time https://example.com

# trace with request body (hidden by default in -v)
curl --trace-ascii - -d "secret=data" https://example.com

# compare verbose vs trace
curl -v https://example.com 2>&1 | head
curl --trace-ascii - https://example.com | head

仅响应头

-I 发送 HEAD 请求(大多数服务器返回与 GET 相同的头但不返回响应体)。-D 将响应头转储到文件或 '-'(stdout)。要查看 curl 发送的内容(请求头),使用 -v 并 grep '^>'。对于仅状态码,-w '%{http_code}' 是最干净的。注意:某些服务器对 HEAD 和 GET 的响应不同。

curl
# HEAD request: response headers only
curl -I https://example.com
curl --head https://example.com

# dump response headers to stdout, discard body
curl -D - -o /dev/null https://example.com

# dump response headers to a file
curl -D headers.txt -o body.html https://example.com

# show request headers (what curl sent)
curl -v -o /dev/null https://example.com 2>&1 | grep '^>'

# show both request and response headers
curl -v -o /dev/null https://example.com 2>&1 | grep -E '^[<>]'

# show only the status line
curl -s -o /dev/null -w "%{http_code} %{http_version}\n" \
     https://example.com
curl -D - -o /dev/null -s https://example.com | head -1

请求与响应分离

详细输出到 stderr,响应体到 stdout,因此可以通过重定向分别捕获。-D 保存响应头;-o 保存响应体;-v(stderr)捕获完整对话。这种分离对调试 API 至关重要:独立检查头和响应体、记录时序,并比较请求与响应。

curl
# capture request and response separately
curl -v https://example.com \
     -o body.txt \
     1> /dev/null 2> verbose.log

# extract request only
curl -v https://example.com 2>&1 1>/dev/null | grep '^>' > request.txt

# extract response only
curl -v https://example.com 2>&1 1>/dev/null | grep '^<' > response.txt

# split headers and body cleanly
curl -s -D headers.txt -o body.txt https://example.com

# capture everything: body, headers, request, timing
curl -s -D headers.txt -o body.txt \
     -w "@curl-format.txt" https://example.com >> timing.log

# show body to terminal, headers to file
curl -D headers.txt https://example.com

调试选项

--dry-run 打印 curl 将执行的操作但不发送——非常适合验证 URL 通配符和配置。--resolve 将主机固定到 IP,绕过 DNS(对测试负载均衡器后面的特定后端至关重要)。--interface 绑定到网卡或 IP。--no-keepalive 强制每次请求新建连接,便于复现连接级 bug。

curl
# show what curl would do without sending
curl --dry-run https://example.com

# dump parsed config files
curl --config /dev/null -v https://example.com 2>&1 | grep -i config

# force a fresh connection (no keep-alive reuse)
curl --no-keepalive https://example.com

# disable DNS cache
curl --doh-url https://dns.example.com/dns-query https://example.com

# use a specific network interface
curl --interface eth0 https://example.com
curl --interface 192.168.1.5 https://example.com

# resolve a hostname to a specific IP (no DNS)
curl --resolve example.com:443:1.2.3.4 https://example.com

# debug globbing (list URLs without fetching)
curl --dry-run -O https://example.com/file[1-3].zip
14

响应信息

HTTP 状态码

-w '%{http_code}' 仅提取状态码用于脚本编程。-f(--fail)使 curl 在 4xx/5xx 时以非零退出(没有它,curl 即使对 404 也返回 0)。--fail-with-body 在错误时仍打印响应体(更便于调试)。在脚本中根据状态码分支:2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务器错误。

curl
# get only the status code
curl -s -o /dev/null -w "%{http_code}" https://example.com
echo   # 200

# status code with newline
curl -s -o /dev/null -w "%{http_code}\n" https://example.com

# full status line
curl -s -o /dev/null -w "%{http_version} %{http_code}\n" https://example.com

# branch on status code
CODE=$(curl -s -o /dev/null -w "%{http_code}" https://example.com)
if [ "$CODE" = "200" ]; then echo "ok"; else echo "fail: $CODE"; fi

# fail on HTTP errors (4xx, 5xx)
curl -f https://example.com || echo "HTTP error"
curl --fail-with-body https://example.com   # 7.76+: still print body

# check redirect chain status codes
curl -L -w "%{num_redirects} redirects, final %{http_code}\n" \
     -o /dev/null -s https://short.url/abc

Write-out 格式

-w(--write-out)使用 %{variable} 占位符格式化传输信息。时序变量(time_namelookup、time_connect、time_appconnect、time_total)分解时间花在哪里——对性能分析至关重要。%{json}(curl 7.70+)将所有变量输出为 JSON。将格式存储在文件中并使用 -w @file 以便重用。

curl
# built-in variables with -w / --write-out
curl -s -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" \
     https://example.com

# URL info
curl -s -o /dev/null -w "URL: %{url_effective}\nIP: %{remote_ip}\n" \
     https://example.com

# timing breakdown
curl -s -o /dev/null \
     -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n" \
     https://example.com

# size info
curl -s -o /dev/null \
     -w "downloaded=%{size_download} uploaded=%{size_upload}\n" \
     https://example.com

# write format from a file
cat > fmt.txt <<EOF
http_code: %{http_code}
time_total: %{time_total}s
EOF
curl -s -o /dev/null -w "@fmt.txt" https://example.com

# JSON output (curl 7.70+)
curl -s -o /dev/null -w "%{json}" https://example.com

响应时间

时序变量分解请求:time_namelookup(DNS)、time_connect(TCP)、time_appconnect(TLS)、time_starttransfer(TTFB——首字节接收时间)、time_total(整个传输)。TTFB 是 API 延迟的关键指标。比较各端点的时序以识别瓶颈(DNS、网络、TLS 或服务器处理)。

curl
# total time
curl -s -o /dev/null -w "%{time_total}\n" https://example.com

# DNS resolution time
curl -s -o /dev/null -w "%{time_namelookup}\n" https://example.com

# TCP connect time
curl -s -o /dev/null -w "%{time_connect}\n" https://example.com

# TLS handshake time (after connect)
curl -s -o /dev/null -w "%{time_appconnect}\n" https://example.com

# time to first byte (TTFB)
curl -s -o /dev/null -w "%{time_starttransfer}\n" https://example.com

# full timing breakdown
curl -s -o /dev/null -w \
  "dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" \
  https://example.com

# compare two endpoints
curl -s -o /dev/null -w "%{time_total}\n" https://api1.example.com
curl -s -o /dev/null -w "%{time_total}\n" https://api2.example.com

提取响应头

-D - 将响应头转储到 stdout;通过管道传给 grep 以提取特定头。-I(HEAD)也可行,但可能与 GET 不同。-w 提供 %{url_effective}(重定向后的最终 URL)、%{remote_ip}(服务器 IP)和 %{num_redirects}。这些对调试 CDN 路由、重定向和服务器识别非常宝贵。

curl
# dump all response headers to stdout, discard body
curl -s -D - -o /dev/null https://example.com

# extract one header (Content-Type)
curl -s -D - -o /dev/null https://example.com | \
  grep -i "^content-type:"

# extract Content-Length (size)
curl -s -I https://example.com/file.zip | grep -i content-length

# extract Set-Cookie
curl -s -D - -o /dev/null https://example.com | grep -i set-cookie

# extract the final URL after redirects
curl -s -L -o /dev/null -w "%{url_effective}\n" https://short.url/abc

# extract the server's IP address
curl -s -o /dev/null -w "%{remote_ip}\n" https://example.com

# count redirects
curl -s -L -o /dev/null -w "%{num_redirects}\n" https://short.url/abc

格式化变量

-w(--write-out)支持数十个变量(运行 'curl -w "%{json}"' 以 JSON 形式查看全部)。时序变量是性能监控的基础;size 变量跟踪带宽;url_effective 和 num_redirects 调试路由。%{json}(curl 7.70+)是捕获所有内容用于日志或仪表板的最简单方式。

curl
# complete list of common -w variables
# %{http_code}        - response status code
# %{http_version}     - HTTP version (1.1, 2, 3)
# %{url_effective}    - final URL after redirects
# %{remote_ip}        - server IP address
# %{remote_port}      - server port
# %{time_namelookup}  - DNS resolution time
# %{time_connect}     - TCP connect time
# %{time_appconnect}  - TLS handshake time
# %{time_pretransfer} - time before transfer starts
# %{time_starttransfer} - TTFB (first byte)
# %{time_total}       - total time
# %{size_download}    - bytes downloaded
# %{size_upload}      - bytes uploaded
# %{size_header}      - header bytes
# %{size_request}     - request bytes
# %{num_redirects}    - number of redirects
# %{ssl_verify_result}- SSL verification result
# %{content_type}     - response content type
# %{json}             - all variables as JSON (7.70+)

# reference: curl -w "%{json}" https://example.com
curl -s -o /dev/null -w "%{json}" https://example.com | jq
15

FTP 与 SMTP

FTP 基础

curl 支持 FTP 下载(GET)和上传(通过 -T 的 PUT)。-u 提供凭据;匿名 FTP 使用 'anonymous' + 邮箱。FTPS(--ftp-ssl)加密控制通道。被动模式(默认)可通过 NAT/防火墙;如服务器要求,使用 --ftp-port 切换为主动模式。

curl
# anonymous FTP download
curl ftp://ftp.example.com/file.txt

# authenticated FTP
curl -u user:pass ftp://ftp.example.com/file.txt

# list a directory
curl ftp://ftp.example.com/directory/

# download a file
curl -O ftp://ftp.example.com/file.txt

# upload a file (PUT)
curl -T local.txt ftp://ftp.example.com/remote.txt -u user:pass

# use explicit FTPS (FTP over TLS)
curl --ftp-ssl ftp://ftp.example.com/file.txt -u user:pass

# passive mode (default; needed behind NAT/firewalls)
curl --ftp-pasv ftp://ftp.example.com/ -u user:pass

FTP 上传

-T 通过 FTP 的 STOR 命令上传文件。--ftp-create-dirs 用 MKD 创建父目录。--append 使用 APPE 追加到现有文件。-C - 恢复中断的上传。通配符({a,b})在一次调用中上传多个文件。对于目录,先 tar 打包——FTP 无法直接上传文件夹。

curl
# upload a single file
curl -T local.txt ftp://ftp.example.com/remote.txt -u user:pass

# upload with explicit filename
curl -T local.txt -o remote.txt ftp://ftp.example.com/ -u user:pass

# upload to a directory
curl -T local.txt ftp://ftp.example.com/uploads/ -u user:pass

# create a directory (MKD command)
curl --ftp-create-dirs -T local.txt \
     ftp://ftp.example.com/newdir/file.txt -u user:pass

# append to a remote file
curl -T local.txt --append ftp://ftp.example.com/remote.txt -u user:pass

# upload multiple files with globbing
curl -T "{file1,file2}.txt" ftp://ftp.example.com/ -u user:pass

# resume an interrupted upload
curl -C - -T bigfile.iso ftp://ftp.example.com/bigfile.iso -u user:pass

FTP 列表

以 / 结尾的 FTP URL 列出目录。--list-only 仅返回文件名。-v 显示底层 FTP 命令(USER、PASS、PASV、LIST、RETR)——对调试权限或防火墙问题非常宝贵。Curl 无法递归进入子目录;递归 FTP 下载请使用 wget --mirror 或脚本。

curl
# list a directory (default format)
curl ftp://ftp.example.com/ -u user:pass

# list with --list-only (names only, no details)
curl --list-only ftp://ftp.example.com/ -u user:pass

# list a specific directory
curl ftp://ftp.example.com/pub/ -u user:pass

# recursive listing (curl doesn't recurse natively)
# use --ftp-method to control PASV/EPSV behavior
curl --ftp-method nocwd ftp://ftp.example.com/ -u user:pass

# download all files in a directory (with globbing)
curl -O ftp://ftp.example.com/pub/*.txt -u user:pass

# show server responses (FTP commands)
curl -v ftp://ftp.example.com/ -u user:pass 2>&1 | grep -E "^\*|^[<>]"

SMTP 发送邮件

curl 可通过 SMTP 发送邮件。响应体(含头)通过 -T 管道传输的文件提供。--mail-from 和 --mail-rcpt 设置信封(与响应体中的 To/From 头分开)。--ssl-reqd 强制 STARTTLS(端口 587);smtps:// 使用隐式 TLS(端口 465)。通过 -u 认证(LOGIN/PLAIN)。适合在没有邮件客户端的服务器上脚本化告警。

curl
# send email via SMTP
curl --url "smtp://smtp.example.com:587" \
     --mail-from "[email protected]" \
     --mail-rcpt "[email protected]" \
     -u "[email protected]:password" \
     -T email.txt

# email body from a file
cat > email.txt <<EOF
From: [email protected]
To: [email protected]
Subject: Test from curl

Hello from curl!
EOF
curl --url "smtp://smtp.example.com:587" \
     --mail-from "[email protected]" \
     --mail-rcpt "[email protected]" \
     -u "[email protected]:password" \
     --ssl-reqd \
     -T email.txt

# multiple recipients
curl --url "smtp://smtp.example.com:587" \
     --mail-from "[email protected]" \
     --mail-rcpt "[email protected]" \
     --mail-rcpt "[email protected]" \
     -T email.txt

# STARTTLS (port 587)
curl --ssl-reqd --url "smtp://smtp.example.com:587" \
     --mail-from "[email protected]" --mail-rcpt "[email protected]" \
     -T email.txt

# SMTPS (implicit TLS, port 465)
curl --url "smtps://smtp.example.com:465" \
     --mail-from "[email protected]" --mail-rcpt "[email protected]" \
     -T email.txt

IMAP 与 POP3

curl 可通过 IMAP 和 POP3 读取邮件。URL 语法包含文件夹、UID 和部分选择器。IMAP 支持搜索(?UNSEEN、?SUBJECT 等)。imaps:// 和 pop3s:// 使用隐式 TLS。注意:curl 对邮件是只读的——它可获取但不能通过 IMAP/POP3 发送(使用 SMTP 发送)。响应体以原始 RFC 822 消息文本返回。

curl
# list IMAP mailboxes
curl -u "user:pass" --url "imap://imap.example.com:143"

# list messages in INBOX
curl -u "user:pass" --url "imap://imap.example.com/INBOX"

# fetch a specific message (UID 1)
curl -u "user:pass" --url "imap://imap.example.com/INBOX;UID=1"

# fetch message headers only
curl -u "user:pass" --url "imap://imap.example.com/INBOX;UID=1;SECTION=HEADER"

# search for unread messages
curl -u "user:pass" --url "imap://imap.example.com/INBOX?UNSEEN"

# POP3: list messages
curl -u "user:pass" --url "pop3://pop.example.com/"

# POP3: retrieve message #1
curl -u "user:pass" --url "pop3://pop.example.com/1"

# use IMAPS (implicit TLS, port 993)
curl -u "user:pass" --url "imaps://imap.example.com/INBOX"

FTPS 与 SFTP

FTP 有三种加密变体:FTPS explicit(--ftp-ssl,在端口 21 升级)、FTPS implicit(ftps://,在端口 990 从一开始就 TLS)和 SFTP(sftp://,端口 22 上完全不同的 SSH 协议)。SFTP 通过 --key 支持密钥认证(用 --pass 处理加密密钥)。如今 SFTP 通常优于 FTPS,因为它更简单、更友好防火墙。

curl
# FTPS: explicit TLS (upgrade to TLS on plain FTP port)
curl --ftp-ssl --ssl-reqd ftp://ftp.example.com/ -u user:pass

# FTPS: implicit TLS (TLS from the start, port 990)
curl ftps://ftp.example.com:990/ -u user:pass

# SFTP (SSH File Transfer Protocol, port 22)
curl -u user sftp://sftp.example.com/file.txt

# SFTP with key authentication
curl -u user --key ~/.ssh/id_rsa \
     sftp://sftp.example.com/file.txt

# SFTP with a password-protected key
curl -u user --key ~/.ssh/id_rsa --pass passphrase \
     sftp://sftp.example.com/file.txt

# SFTP upload
curl -u user -T local.txt sftp://sftp.example.com/remote.txt

# SFTP list directory
curl -u user sftp://sftp.example.com/
16

并行与批量

并行下载

Curl 本身是顺序的——要并发下载,通过 & 和 wait 在后台运行多个 curl,或使用 xargs -P N 一次运行 N 个。这能显著加快许多小文件的下载。请考虑周到:过多并行连接可能压垮服务器或让你被限流。4-8 个并发是合理的默认值。

curl
# background multiple curls (shell parallelism)
curl -O https://example.com/a.zip &
curl -O https://example.com/b.zip &
curl -O https://example.com/c.zip &
wait

# parallel with xargs (4 concurrent)
echo -e "https://example.com/a.zip\nhttps://example.com/b.zip" | \
  xargs -n1 -P4 curl -O

# parallel with a URL list file
xargs -n1 -P4 curl -O < urls.txt

# zsh-specific parallel with {..}
for url in https://example.com/{a,b,c}.zip; do
  curl -O "$url" &
done; wait

# bash-specific using arrays and &
urls=("https://example.com/a.zip" "https://example.com/b.zip")
for url in "${urls[@]}"; do curl -O "$url" & done; wait

xargs 并行

xargs -P N 并发运行 N 个进程,将 stdin 的每一行作为参数喂入。-a 从文件读取。-I {} 让你将 URL 放在命令的任何位置。这是无需额外工具并行化 curl 下载的最简单方式。对于主机感知的限流或复杂重试逻辑,使用专用下载管理器如 aria2c。

curl
# download each URL from a file, 4 concurrent
xargs -n1 -P4 -a urls.txt curl -O

# download with custom names, parallel
cat urls.txt | xargs -n2 -P4 sh -c 'curl -o "$0" "$1"'

# download and process each response
cat urls.txt | xargs -n1 -P4 -I {} sh -c \
  'curl -s {} | jq .field'

# retry failures in parallel
xargs -n1 -P4 -a urls.txt curl --retry 3 -O

# limit total concurrent connections to a host
# (xargs doesn't know about hosts; use a queue or a download manager)
xargs -n1 -P4 -a urls.txt curl -O --limit-rate 1M

# show progress for parallel downloads
xargs -n1 -P4 -a urls.txt curl -O --progress-bar 2>&1

URL 通配符

通配符将 URL 展开为多个请求:[1-10] 范围(带可选 :step 和通过前导零的零填充)和 {a,b,c} 列表。-o 'file#1.zip' 使用 #1 作为计数器自定义输出文件名。-g(--globoff)禁用展开——对包含字面 [ ] 或 { } 的 URL(JSON、IPv6)至关重要。

curl
# numeric range: 1, 2, 3
curl -O https://example.com/file[1-3].zip

# range with step: 0, 5, 10, 15, 20
curl -O https://example.com/file[0-20:5].zip

# leading zeros (zero-padded)
curl -O https://example.com/file[01-10].zip   # 01, 02, ..., 10

# list expansion: a, b, c
curl -O https://example.com/{a,b,c}.zip

# nested globs
curl -O https://example.com/{v1,v2}/file[1-2].txt

# save each with a custom name pattern
curl -o "file#1.zip" https://example.com/file[1-3].zip

# disable globbing (literal brackets)
curl -g "https://example.com/file[1].zip"

顺序范围

对于顺序下载(并行会压垮服务器或违反速率限制时),在 bash 中循环 URL。添加 sleep 以示礼貌。-sf(静默 + 失败)使 curl 在 HTTP 错误时以非零退出,以便循环提前中断。这是爬取或抓取的安全模式:尊重、添加延迟、处理错误。

curl
# fetch URLs one at a time (no parallelism)
for i in 1 2 3 4 5; do
  curl -O "https://example.com/page$i.html"
done

# range with brace expansion (bash/zsh)
for i in {1..10}; do
  curl -s -O "https://example.com/page$i.html"
done

# with a delay between requests (rate limiting)
for i in {1..10}; do
  curl -s -O "https://example.com/page$i.html"
  sleep 1
done

# retry each URL individually
for url in $(cat urls.txt); do
  curl -s --retry 3 -O "$url"
done

# conditional: stop on first error
for url in $(cat urls.txt); do
  curl -sf -O "$url" || { echo "failed: $url"; break; }
done

GNU Parallel 与 curl

GNU parallel 比 xargs 更强大:它保留输出顺序、显示进度(--bar)并更灵活地处理参数。-j N 设置并发。:::: 从文件读取,::: 从命令行读取。Parallel 非常适合批量 API 测试、抓取和基准测试——你需要从并发请求中获得干净、有序的输出。

curl
# GNU parallel: smarter than xargs (preserves order, progress)
parallel -j4 curl -O {} ::: https://example.com/a.zip \
                            https://example.com/b.zip

# parallel from a file
parallel -j4 curl -O :::: urls.txt

# parallel with progress bar
parallel --bar -j4 curl -O :::: urls.txt

# parallel with output capture
parallel -j4 'curl -s -o {}.out {}' :::: urls.txt

# parallel with retry and timing
parallel -j4 'curl -s -w "{} %{time_total}s\n" -o /dev/null {}' :::: urls.txt

# parallel POST requests
parallel -j4 'curl -s -X POST -d "id={}" https://api.example.com/{}' \
  ::: 1 2 3 4 5

# combine with --dry-run to preview
parallel --dry-run -j4 curl -O :::: urls.txt
17

配置文件

.curlrc 基础

~/.curlrc 保存应用于每次 curl 调用的默认选项。使用不带前导 -- 的长选项名。每行一个选项;值可加引号。这让常见设置(超时、重试、User-Agent)在脚本和交互使用中保持一致。在命令行显式传递选项可按请求覆盖。

curl
# ~/.curlrc is read automatically by curl
# one option per line, long option names without the --

# ~/.curlrc example:
# show progress by default
silent
# always follow redirects
location
# default User-Agent
user-agent = "MyApp/1.0"
# default headers
header = "Accept: application/json"
# retry transient errors
retry = 3
# connect timeout
connect-timeout = 10
# max time
max-time = 30

# lines starting with # are comments
# blank lines are ignored

# test: curl https://example.com now uses all the above

配置文件位置

Curl 默认读取 ~/.curlrc(或 $CURL_HOME/.curlrc)。-K(--config)指向特定文件;-K - 从 stdin 读取。-q(--disable)完全跳过配置文件——当默认选项破坏特定请求时很有用。CURL_HOME 为便携式设置重定位搜索目录。

curl
# default locations (searched in order):
# 1. ~/.curlrc (or $CURL_HOME/.curlrc)
# 2. /etc/curl/curlrc (system-wide, if compiled in)

# set a custom config directory
export CURL_HOME=/etc/curl
# curl then reads $CURL_HOME/.curlrc

# specify a config file explicitly
curl -K /path/to/myconfig https://example.com
curl --config /path/to/myconfig https://example.com

# read config from stdin
echo "silent" | curl -K - https://example.com

# disable config file reading
curl -q https://example.com
curl --disable https://example.com

# check which config curl reads
curl -v https://example.com 2>&1 | grep -i config

使用 -K 配置

-K 从文件加载选项,非常适合按环境配置(开发 vs 生产令牌、超时、头)。将机密存储在配置文件中(chmod 600)可让其远离 shell 历史和 ps。多个 -K 文件按顺序处理,因此后面的文件覆盖前面的——适合基础配置加环境特定覆盖。

curl
# use a config file for a specific request
curl -K prod.conf https://api.example.com/data

# prod.conf:
# header = "Authorization: Bearer prod-token"
# header = "X-Environment: production"
# connect-timeout = 5
# max-time = 20

# dev.conf (different settings)
# header = "Authorization: Bearer dev-token"
# header = "X-Environment: development"
# max-time = 60

# switch environments by config file
curl -K dev.conf https://api.example.com/data
curl -K prod.conf https://api.example.com/data

# config file with URL
# echo "url = https://api.example.com/data" > req.conf
curl -K req.conf

# multiple config files (later ones override earlier)
curl -K defaults.conf -K override.conf https://example.com

常见默认值

一个可靠的 ~/.curlrc 让每次 curl 调用更安全、更一致:超时防止挂起、重试处理瞬时失败、--compressed 透明处理 gzip、剥离的 User-Agent 减少指纹识别。write-out 行记录每次请求的时序和状态——对快速性能检查非常宝贵。

curl
# ~/.curlrc with sensible production defaults

# always follow redirects
location
# limit redirects
max-redirs = 5

# timeouts (fail fast, cap total)
connect-timeout = 10
max-time = 60

# retry transient failures
retry = 3
retry-delay = 2
retry-all-errors

# show errors even when silent
show-error

# auto-decompress responses
compressed

# negotiate HTTP/2
http2

# verify SSL by default (explicit for clarity)
cacert = /etc/ssl/certs/ca-certificates.crt

# don't leak the curl version
user-agent = ""

# verbose timing on failure
write-out = "time: %{time_total}s code: %{http_code}\n"

多配置文件

将配置拆分为分层文件(base、auth、api、environment)可保持模块化和安全。按顺序使用多个 -K 标志处理——后面的文件覆盖前面的。将机密放在版本控制之外的 600 权限文件中。加载整个堆栈的包装脚本让你用所有默认值调用 'mycurl'。

curl
# base.conf — shared defaults
# silent
# show-error
# compressed
# connect-timeout = 10
# max-time = 30

# auth.conf — credentials (chmod 600)
# user = alice:secret
# header = "Authorization: Bearer TOKEN"

# api.conf — API-specific options
# header = "Accept: application/json"
# header = "X-Client: my-app/2.0"
# retry = 5

# combine configs in order (later overrides earlier)
curl -K base.conf -K auth.conf -K api.conf \
     https://api.example.com/data

# config file referencing another (nesting not supported)
# but you can source them in a wrapper script:
#   #!/bin/bash
#   curl -K base.conf -K auth.conf -K api.conf "$@"

# environment-specific overlay
curl -K base.conf -K "${ENV}.conf" https://api.example.com
18

高级技巧

连接复用

在单次 curl 调用内,TCP 连接在同一主机的多个 URL 之间复用(HTTP keep-alive),节省 TLS 握手开销。--no-keepalive 禁用此行为。--max-connects 限制多 URL 获取的并发连接数。跨独立的 curl 调用没有连接复用——为此使用持久客户端如 httpie 或 Python/Node 脚本。

curl
# curl reuses connections within a single invocation
# multiple URLs in one command share the TCP connection
curl -o a.txt https://example.com/a \
     -o b.txt https://example.com/b

# keep-alive is on by default
# disable it for a single request
curl --no-keepalive https://example.com

# limit max connections (for multi-URL)
curl --max-connects 4 \
     -O https://example.com/a -O https://example.com/b

# persistent session across shell calls (not natively supported)
# but you can use a config file to reduce overhead
curl -K session.conf https://example.com/a
curl -K session.conf https://example.com/b

# check if connection was reused
curl -v https://example.com 2>&1 | grep -i "Connection #0"

HTTP/2 与 HTTP/3

HTTP/2 在一个连接上多路复用请求;HTTP/3 使用基于 UDP 的 QUIC,消除队头阻塞。--http2 通过 TLS 上的 ALPN 协商;--http2-prior-knowledge 跳过协商(用于明文 h2c)。--http3 需要特殊构建的 curl。检查 %{http_version} 确认实际使用的版本。大多数现代 CDN 默认支持 h2。

curl
# negotiate HTTP/2 (needs TLS + ALPN)
curl --http2 https://example.com

# force HTTP/2 (fail if not available)
curl --http2-only https://example.com

# negotiate HTTP/3 (QUIC, needs curl built with quiche/openssl-quic)
curl --http3 https://example.com

# force HTTP/3 only
curl --http3-only https://example.com

# check what version was negotiated
curl -s -o /dev/null -w "%{http_version}" https://example.com
echo   # 2 or 3

# list supported HTTP versions
curl --version | grep -i -E "http2|http3"

# HTTP/2 with prior knowledge (no upgrade negotiation)
curl --http2-prior-knowledge http://example.com

条件请求

条件请求节省带宽:如果资源未更改,服务器返回 304 Not Modified(无响应体)。-z(--time-cond)从日期或本地文件的 mtime 设置 If-Modified-Since。对于基于 ETag 的缓存,手动设置 If-None-Match。If-Unmodified-Since 启用乐观并发——如果其他人修改了资源,PUT 失败。

curl
# fetch only if modified since a date
curl -z "Wed, 21 Oct 2025 07:28:00 GMT" \
     -O https://example.com/data.json
curl --timecond "Wed, 21 Oct 2025 07:28:00 GMT" \
     -O https://example.com/data.json

# fetch only if changed (use a local file's mtime)
curl -z local_copy.json -O https://example.com/data.json

# ETag-based conditional (If-None-Match)
curl -H 'If-None-Match: "abc123"' \
     -o data.json https://example.com/data.json
# server returns 304 Not Modified if ETag matches

# If-Modified-Since header manually
curl -H "If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT" \
     https://example.com/data.json

# If-Unmodified-Since (for concurrency control)
curl -X PUT -H "If-Unmodified-Since: Wed, 21 Oct 2025 07:28:00 GMT" \
     -d '{"x":1}' https://api.example.com/data

接口与 DNS

--interface 绑定到网卡或 IP——在多宿主机器上很有用。--resolve 将主机固定到 IP,绕过 DNS,非常适合测试负载均衡器后面的后端。--doh-url 使用 DNS-over-HTTPS(隐私)。-4/-6 强制 IP 协议族。Happy Eyeballs(默认)并行尝试 IPv4 和 IPv6 以避免 IPv6 黑洞延迟。

curl
# bind to a specific network interface
curl --interface eth0 https://example.com
curl --interface 192.168.1.5 https://example.com

# pin a hostname to an IP (skip DNS)
curl --resolve example.com:443:1.2.3.4 https://example.com

# pin multiple hosts
curl --resolve example.com:443:1.2.3.4 \
     --resolve api.example.com:443:1.2.3.5 \
     https://example.com/

# use a custom DNS server (DoH)
curl --doh-url https://dns.google/dns-query https://example.com
curl --doh-url https://cloudflare-dns.com/dns-query https://example.com

# use a specific DNS resolver (plain DNS, curl 7.84+)
curl --dns-servers 8.8.8.8,1.1.1.1 https://example.com

# happy eyeballs: try IPv4 and IPv6 in parallel
curl --happy-eyeballs-timeout-ms 200 https://example.com

# force IPv4 or IPv6
curl -4 https://example.com
curl -6 https://example.com

范围请求与部分下载

范围请求(-r)获取文件的一部分,返回 206 Partial Content。用例:预览大文件、并行多连接下载(拆分为范围、并发获取、拼接)和恢复中断的传输(-C -)。服务器必须支持 Accept-Ranges: bytes。这是 aria2 等下载加速器的基础。

curl
# request a byte range (first 1024 bytes)
curl -r 0-1023 -o part.bin https://example.com/file.zip

# request from byte 1024 to end
curl -r 1024- -o rest.bin https://example.com/file.zip

# request last 500 bytes
curl -r -500 -o tail.bin https://example.com/file.zip

# check if server supports ranges
curl -I https://example.com/file.zip | grep -i accept-ranges

# download in parallel chunks
curl -r 0-49999999    -o part1.bin https://example.com/big.iso &
curl -r 50000000-     -o part2.bin https://example.com/big.iso &
wait
cat part1.bin part2.bin > big.iso

# resume a partial download
curl -C - -o file.zip https://example.com/file.zip

# HTTP 206 = Partial Content (range succeeded)
curl -r 0-100 -v https://example.com/file.zip 2>&1 | grep HTTP

退出码

Curl 的退出码区分失败模式:6(DNS)、7(连接)、22(HTTP 错误,仅与 -f 一起)、28(超时)、35(TLS)、60(证书)。使用这些构建健壮的脚本,适当重试(例如对 28 重试但对 6 快速失败)。--fail-with-body 即使在 4xx/5xx 时也返回响应体,使 API 错误调试比 -f 容易得多。

curl
# common curl exit codes:
# 0  - success
# 1  - unsupported protocol
# 3  - malformed URL
# 6  - couldn't resolve host (DNS failure)
# 7  - failed to connect
# 22 - HTTP error (use with -f)
# 26 - couldn't read a file (e.g., -d @missing.txt)
# 28 - operation timeout
# 35 - SSL connect error
# 47 - too many redirects
# 52 - server didn't reply anything
# 55 - failed sending network data
# 56 - failed receiving network data
# 60 - certificate verification failed

# check exit code
curl -sf https://example.com || echo "failed: $?"

# branch on specific codes
curl --max-time 5 https://example.com
case $? in
  0)  echo "ok" ;;
  6)  echo "DNS failure" ;;
  7)  echo "connection refused" ;;
  28) echo "timeout" ;;
  *)  echo "error: $?" ;;
esac

# treat HTTP errors as failures
curl -f https://api.example.com || echo "HTTP error"

# still print body on HTTP error (7.76+)
curl --fail-with-body https://api.example.com

这篇内容对您有帮助吗?