入门
基本请求
curl 从命令行发起 HTTP 请求,默认使用 GET 方法。-v 显示详细输出,-o 保存到指定文件名,-O 保留远程文件名,-L 跟随重定向,-I 仅获取响应头(HEAD 请求)。
# 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 丢弃响应体。
# 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 配合可在不保存响应体的情况下检查响应头。
# 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 会将其发送到所有主机(存在安全风险)。
# 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.comURL 语法与通配符
curl 展开 URL 的 'globbing' 模式:[1-10] 范围、{a,b,c} 列表,可带可选的 :step 步长。每次展开是一个独立的请求。使用 -g(--globoff)可将方括号和大括号按字面量处理——这对包含 JSON 数组或 IPv6 地址(如 http://[::1]:8080/)的 URL 至关重要。
# 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 需要特殊构建)。
# 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 http2HTTP 方法
GET 请求
GET 是默认方法。始终给包含 & 或 ? 的 URL 加引号,以防 shell 后台化或展开它们。-X 显式设置方法,但对 GET 通常不必要。对于 API,设置 Accept 头来协商响应格式。
# 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/secretPOST 请求
发送 -d 会自动将方法切换为 POST,因此 -X POST 是冗余的(但无害且更清晰)。要 POST 文件,使用 -d @filename(@ 触发文件读取)。-d 的默认 Content-Type 是 application/x-www-form-urlencoded;用 -H 覆盖以发送 JSON。
# 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/usersPUT 与 PATCH
PUT 替换整个资源;PATCH 应用部分更新。两者都需要 -X,因为 curl 没有对 应的 -d 快捷方式。-T(upload-file)以流式上传文件作为请求体,非常适合 PUT 上传。发送原始文件时使用 --data-binary 而非 -d,以避免去除换行符。
# 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.txtDELETE
DELETE 用于删除资源。大多数 API 要求认证,因此需包含 -u 或 Authorization 头。某些 API 接受请求体(reason、级联标志)——添加 -d 和 Content-Type 头。使用 -w '%{http_code}' 检查结果:204(No Content)和 200 是典型的成功状态。
# 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/1HEAD 与 OPTIONS
-I 发送 HEAD 请求(仅响应头,无响应体)——适合在不下载的情况下检查大小(Content-Length)、类型(Content-Type)和最后修改时间。OPTIONS 揭示允许的方法(Allow 头),并被浏览器用于 CORS 预检。添加 Origin 和 Access-Control-Request-Method 头可模拟真实的预检请求。
# 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请求头
设置请求头 (-H)
-H(--header)添加请求头。多个头使用多个 -H 重复指定。末尾分号(X-Empty;) 发送空值。@headers.txt 从文件读取头(每行一个 'Name: value')——便于将 Authorization 令牌排除在 shell 历史之外。
# 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)并自动解压,因此你无需手动处理编码。
# 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.comUser-Agent
一些服务器会屏蔽默认的 curl User-Agent。-A(--user-agent)设置它;空 -A 会完全移除它。长格式是 -H 'User-Agent: ...'。许多反爬站点会检查真实浏览器签名,因此通常需要逼真的浏览器 UA。
# 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 ~/.curlrcReferer 与 Host
-e(--referer)设置 Referer 头。覆盖 Host 在通过 IP 测试虚拟主机时至关重要:服务器根据 Host 选择正确的站点。支持 CORS 的端点通常需要 Origin;X-Forwarded-For/Proto 可模拟代理或负载均衡器后面的请求。
# 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 会替换该头而非发送重复项。这在发送最简请求时很有用。
# 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.comPOST 数据与表单
表单数据 (-d)
-d(--data)发送 URL 编码的表单数据并将方法切换为 POST。多个 -d 标志会用 & 连接。-d 会去除首尾空白和换行符;--data-binary 保留它们;--data-raw 完全禁用 @file 功能(因此以 @ 开头的字面值会原样发送)。
# 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-typeURL 编码
--data-urlencode 对值(或整个 name=value)进行百分号编码,因此你无需手动编码空格、&、= 或非 ASCII 字符。使用 name@file 时,文件内容会作为值被 URL 编码。对于用户 supplied 数据始终使用此选项,以避免破坏表单结构。
# 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。
# 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。
# 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.comContent-Type 与表单
始终让 Content-Type 与请求体格式匹配:application/x-www-form-urlencoded(-d 的默认值)、application/json(配合 -H)、multipart/form-data(由 -F 自动设 置)、SOAP 用 text/xml。为非 ASCII 数据添加 charset=utf-8 是良好实践。不匹配会导致服务器拒绝或错误解析请求体。
# 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.comJSON 请求
基本 JSON POST
JSON 请求需要 -H 'Content-Type: application/json' 加上带 JSON 字符串的 -d。在 JSON 周围使用单引号,这样 shell 不会触碰内部的双引号。通过管道将响应传给 jq '.' 获得可读输出,或 jq '.field' 提取特定值。
# 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 . 验证——语法错误会浪费一次请求。
# 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 泄露。
# 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.comGraphQL 请求
GraphQL 使用单个 POST 端点,请求体是包含 'query' 字符串和可选 'variables' 的 JSON。内联编写查询需要转义内部双引号,因此将复杂查询存储在 .json 文件中并使用 -d @file。内省(__schema 查询)列出 API 的类型,非常适合探索。
# 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 以确认协商生效。
# 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文件上传
Multipart 上传 (-F)
-F(--form)构建 multipart/form-data 请求——文件上传的标准方式。-F file=@path 读取文件。添加 ;filename= 覆盖服务器看到的文件名,;type= 设置该部分的 Content-Type。同一字段名中的多个文件(许多框架中使用 [])需要多个 -F 标志。
# 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 打包。
# 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 的镜像行为)。
# 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} 获取一行摘要。
# 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 期望字符串但你的数据在文件中时很有用。
# 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认证
Basic 认证 (-u)
-u(--user)发送 HTTP Basic 认证(base64 编码的 user:pass)。省略密码会触发交互式提示(更安全——避免出现在 shell 历史中)。--netrc 从 ~/.netrc 读取凭据,因此它们永远不会出现在命令行上。Basic 在明文 HTTP 上不安全;始终使用 HTTPS。任何有 shell 访问权限的人都可通过 ps 看到 -u 参数。
# 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/secretBearer 令牌
Bearer 令牌(OAuth2、JWT)放在 Authorization 头中。避免将令牌放在命令行上——ps 会将其暴露给其他用户。从文件(-H @file)或环境变量读取,对于敏感值使用头文件。refresh-token 片段展示了标准的 OAuth2 客户端凭据流程。
# 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/dataAPI 密钥(Header)
API 密钥出现在自定义头(X-API-Key、X-Client-Id)或作为查询参数。查询字符串会泄露到服务器日志和浏览器历史中,因此优先使用头。对于 AWS,--aws-sigv4 用你的 access/secret 密钥签署请求。GitHub 使用 Bearer 加个人访问令牌(ghp_...)。
# 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/dataNetrc 文件
--netrc 从 ~/.netrc 读取凭据,将主机映射到登录名/密码对。文件必须 chmod 600,否则 curl 拒绝使用。--netrc-file 指向自定义位置。'default' 条目适用于任何没有特定 machine 条目的主机。这是让机密信息远离命令行的最干净方式。
# ~/.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 guestDigest 与 NTLM
Basic 以明文发送凭据(在 HTTPS 下没问题)。Digest 发送哈希,从不发送密码。NTLM 和 Negotiate 处理 Windows/Kerberos SSO。--anyauth 让 curl 根据服务器的 WWW-Authenticate 头选择方法。服务器必须支持该方法;-v 显示它发出的挑战。
# 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-authenticateOAuth 与令牌
OAuth2 有多种授权类型:client_credentials(服务器到服务器)、authorization_code(浏览器登录后)、password(旧版)。令牌端点返回 access_token(通常还有 refresh_token);将 access_token 作为 Bearer 头用于后续请求。应用密码(GitLab、GitHub)可与 -u 配合使用,绕过 2FA 限制。
# 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代理
HTTP/HTTPS 代理
-x(--proxy)通过代理路由请求。http_proxy/https_proxy/no_proxy 环境变量会被自动遵循。对于通过 HTTP 代理访问 HTTPS 目标,curl 使用 CONNECT 建立隧道。设置 no_proxy 可绕过内部主机的代理——逗号分隔,前导点表示子域。
# 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 proxySOCKS 代理
SOCKS5 与协议无关,因此可隧道传输 HTTP、HTTPS 等。--socks5-hostname 让代理解析 DNS(适用于你的机器无法解析的主机)。一个常见技巧:ssh -D 1080 创建本地 SOCKS 代理,通过远程 SSH 服务器隧道传输流量——适合在不受信任的网络上安全浏览。
# 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(无密码)会交互式提示。
# 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.comNo Proxy 与绕过
--noproxy(或 no_proxy 环境变量)列出绕过代理的主机。前导点表示'此域及其所有子域'。'*' 对所有主机绕过代理。CIDR 范围在较新的 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 而你想要端到端隧道时,这很有用。
# 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.comSSL/TLS
跳过证书验证 (--insecure)
-k(--insecure)跳过证书验证——仅用于自签名的开发证书,绝不要在生产中使用,因为它允许中间人攻击。默认验证证书链和主机名。--resolve 让你在保留正确 Host/SNI 的同时固定一个 IP——适合测试负载均衡器后面的特定后端。
# 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 在零信任和服务网格设置中很常见。
# 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.comTLS 版本
--tlsv1.X 设置最低版本;--tls-max 设置上限。强制 1.2+ 可禁用有已知弱点的旧协议(SSLv3、TLS 1.0/1.1)。默认协商双方都支持的最高版本。-v 在 'SSL connection using' 行中揭示协商的版本和密码套件。
# 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 包)可修复此问题。
# 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 是证书检查的更强大替代方案。
# 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下载
保存到文件 (-o/-O)
-o 写入自定义文件名;-O 使用远程文件名(URL 的 basename)。--output-dir 为 -O 设置目标文件夹。多个 -O 标志在一次调用中下载多个文件。-o /dev/null 丢弃响应体——适用于触发 webhook 或测量时序而不保存数据。
# 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 重试并恢复,避免浪费带宽。
# 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} 为脚本提供干净的一行摘要。
# 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 等下载加速器的基础。
# 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。这能显著加快从同一主机下载许多小文件的速度(请尊重服务器的速率限制)。
# 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 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超时与重试
连接超时
--connect-timeout 限制建立 TCP/TLS 连接的时间(不包括传输)。没有它,curl 可能在不可达主机上挂起数分钟(操作系统默认约 2 分钟)。在脚本中始终同时设置 --connect-timeout(对死主机快速失败)和 --max-time(限制整个操作)以避免挂起。
# 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 以防止在缓慢或卡住的服务器上无限挂起。
# 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 - 配合以恢复部分下载。这对健壮的脚本至关重要。
# 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 配合,以免重试永远进行。
# 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 配合以自动恢复停滞的下载。
# 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调试
详细模式 (-v)
-v(verbose)是主要的调试工具。'>' 行是请求,'<' 行是响应,'*' 行是 curl 内部信息(DNS、TLS、连接)——全部在 stderr 上,因此 stdout 保持干净的响应体。重定向 2>&1 以用 grep 过滤。使用 -o /dev/null 检查响应头而不保存响应体。
# 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.comTrace
--trace 转储所有内容(包括响应体)为十六进制 + ASCII;--trace-ascii 跳过十六进制。--trace-time 添加时间戳。与 -v 不同,trace 显示请求和响应体,因此它是调试二进制或压缩数据的首选。警告:--trace 会记录机密信息(密码、令牌)——分享前请清除。
# 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 的响应不同。
# 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 至关重要:独立检查头和响应体、记录时序,并比较请求与响应。
# 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。
# 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响应信息
HTTP 状态码
-w '%{http_code}' 仅提取状态码用于脚本编程。-f(--fail)使 curl 在 4xx/5xx 时以非零退出(没有它,curl 即使对 404 也返回 0)。--fail-with-body 在错误时仍打印响应体(更便于调试)。在脚本中根据状态码分支:2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务器错误。
# 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/abcWrite-out 格式
-w(--write-out)使用 %{variable} 占位符格式化传输信息。时序变量(time_namelookup、time_connect、time_appconnect、time_total)分解时间花在哪里——对性能分析至关重要。%{json}(curl 7.70+)将所有变量输出为 JSON。将格式存储在文件中并使用 -w @file 以便重用。
# 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 或服务器处理)。
# 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 路由、重定向和服务器识别非常宝贵。
# 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+)是捕获所有内容用于日志或仪表板的最简单方式。
# 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 | jqFTP 与 SMTP
FTP 基础
curl 支持 FTP 下载(GET)和上传(通过 -T 的 PUT)。-u 提供凭据;匿名 FTP 使用 'anonymous' + 邮箱。FTPS(--ftp-ssl)加密控制通道。被动模式(默认)可通过 NAT/防火墙;如服务器要求,使用 --ftp-port 切换为主动模式。
# 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:passFTP 上传
-T 通过 FTP 的 STOR 命令上传文件。--ftp-create-dirs 用 MKD 创建父目录。--append 使用 APPE 追加到现有文件。-C - 恢复中断的上传。通配符({a,b})在一次调用中上传多个文件。对于目录,先 tar 打包——FTP 无法直接上传文件夹。
# 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:passFTP 列表
以 / 结尾的 FTP URL 列出目录。--list-only 仅返回文件名。-v 显示底层 FTP 命令(USER、PASS、PASV、LIST、RETR)——对调试权限或防火墙问题非常宝贵。Curl 无法递归进入子目录;递归 FTP 下载请使用 wget --mirror 或脚本。
# 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)。适合在没有邮件客户端的服务器上脚本化告警。
# 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.txtIMAP 与 POP3
curl 可通过 IMAP 和 POP3 读取邮件。URL 语法包含文件夹、UID 和部分选择器。IMAP 支持搜索(?UNSEEN、?SUBJECT 等)。imaps:// 和 pop3s:// 使用隐式 TLS。注意:curl 对邮件是只读的——它可获取但不能通过 IMAP/POP3 发送(使用 SMTP 发送)。响应体以原始 RFC 822 消息文本返回。
# 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,因为它更简单、更友好防火墙。
# 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/并行与批量
并行下载
Curl 本身是顺序的——要并发下载,通过 & 和 wait 在后台运行多个 curl,或使用 xargs -P N 一次运行 N 个。这能显著加快许多小文件的下载。请考虑周到:过多并行连接可能压垮服务器或让你被限流。4-8 个并发是合理的默认值。
# 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; waitxargs 并行
xargs -P N 并发运行 N 个进程,将 stdin 的每一行作为参数喂入。-a 从文件读取。-I {} 让你将 URL 放在命令的任何位置。这是无需额外工具并行化 curl 下载的最简单方式。对于主机感知的限流或复杂重试逻辑,使用专用下载管理器如 aria2c。
# 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>&1URL 通配符
通配符将 URL 展开为多个请求:[1-10] 范围(带可选 :step 和通过前导零的零填充)和 {a,b,c} 列表。-o 'file#1.zip' 使用 #1 作为计数器自定义输出文件名。-g(--globoff)禁用展开——对包含字面 [ ] 或 { } 的 URL(JSON、IPv6)至关重要。
# 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 错误时以非零退出,以便循环提前中断。这是爬取或抓取的安全模式:尊 重、添加延迟、处理错误。
# 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; }
doneGNU Parallel 与 curl
GNU parallel 比 xargs 更强大:它保留输出顺序、显示进度(--bar)并更灵活地处理参数。-j N 设置并发。:::: 从文件读取,::: 从命令行读取。Parallel 非常适合批量 API 测试、抓取和基准测试——你需要从并发请求中获得干净、有序的输出。
# 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配置文件
.curlrc 基础
~/.curlrc 保存应用于每次 curl 调用的默认选项。使用不带前导 -- 的长选项名。每行一个选项;值可加引号。这让常见设置(超时、重试、User-Agent)在脚本和交互使用中保持一致。在命令行显式传递选项可按请求覆盖。
# ~/.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 为便携式设置重定位搜索目录。
# 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 文件按顺序处理,因此后面的文件覆盖前面的——适合基础配置加环境特定覆盖。
# 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 行记录每次请求的时序和状态——对快速性能检查非常宝贵。
# ~/.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'。
# 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高级技巧
连接复用
在单次 curl 调用内,TCP 连接在同一主机的多个 URL 之间复用(HTTP keep-alive),节省 TLS 握手开销。--no-keepalive 禁用此行为。--max-connects 限制多 URL 获取的并发连接数。跨独立的 curl 调用没有连接复用——为此使用持久客户端如 httpie 或 Python/Node 脚本。
# 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。
# 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 失败。
# 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 黑洞延迟。
# 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 等下载加速器的基础。
# 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 容易得多。
# 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相关 curl 代码片段
Copy-paste ready code for common tasks.
HTTP Methods
GET, POST, PUT, and DELETE requests.
Headers
Set and inspect request and response headers.
Authentication
Basic, bearer, and OAuth credentials.
Cookies
Send, save, and reuse cookies.
Upload and Download
Transfer files to and from a server.
Proxies
Route requests through HTTP or SOCKS proxies.
Follow Redirects
Chase 3xx responses automatically.
Debugging
Trace requests, timing, and transfers.
这篇内容对您有帮助吗?