Geonode logo
Geonode Team

Geonode Team

更新于:2026年10月7日

发布于:2026年9月2日

《curl 入门:完整指南》

curl 用于获取 URL 并输出结果。其余均为选项,总数超过两百个。 要高效使用它,你只需要掌握其中大约八个选项。本指南将介绍这八个选项、帮助你理解其余选项的思维模型,以及初学者常犯的几个常见错误。 读完本指南后,你将能够发送请求、解析响应、调试实际传输的数据,并知道接下来该查阅哪份手册。

关于本文作者的简要说明。我们是 Geonode,主要销售代理服务,因此在排查问题时,我们通常会建议大家使用 curl 工具。给初学者的坦诚说明:curl 并不是代理工具,学习它也不需要代理。 下文介绍的所有操作均可通过您自己的网络连接,针对公共端点免费进行。代理的作用要到后期才会显现,例如当您的请求量大到被目标服务器限流时,或者当您需要查看其他国家/地区用户看到的页面时。这两者都不是初学者会遇到的问题。请先掌握这个工具。

什么是 curl 以及它的用途

curl 是一个用于通过 URL 传输数据的命令行程序。其手册中描述它支持“DICT、FILE、FTP、FTPS、GOPHER、GOPHERS、HTTP、HTTPS、IMAP、IMAPS、LDAP、 LDAPS、MQTT、MQTTS、POP3、POP3S、RTSP、SCP、SFTP、SMB、SMBS、SMTP、SMTPS、TELNET、TFTP、WS 和 WSS”——尽管实际上几乎所有人都将其用于 HTTP 和 HTTPS。

主要用途:

  • 从终端或脚本调用 API
  • 检查 URL 是否有效,以及返回的内容
  • 查看服务器返回的确切内容,包括所有头部信息
  • 下载文件
  • 调试:在应用程序外部重现请求,以确定问题出在代码还是服务器上

它不是什么:浏览器。它不执行 JavaScript,不渲染任何内容,也不会维持会话(除非您明确指示)。在浏览器中看起来内容丰富的页面,在 curl 中可能返回近乎空白的骨架,这属于正常现象,而非故障。

您的第一个请求

curl https://example.com

这将执行一个 GET 请求,并将响应正文输出到终端。如果输出是一大段 HTML 代码,说明 curl 运行正常。

以下是四种立即可用的变体:

同时查看响应头和正文,使用 -i

,相关文档见 -i, --show-headers

:“在输出中显示响应头。”

curl -i https://example.com

保存到文件,使用 -o

(您指定的文件名)或 -O

(远程文件名):

curl -o page.html https://example.com
curl -O https://example.com/file.zip

跟随重定向,使用 -L

:“跟随 HTTP 重定向,并使用最初指定的方法重复请求。”如果不使用该选项,curl 会在第一次重定向时停止,并显示重定向页面而非目标页面。

curl -L https://example.com

使用 -sS

实现 保持静默但仍报告错误。-s

可隐藏进度条,-S

可保留错误消息。二者结合正是任何脚本中所需的配置。

curl -sS https://example.com

如果您只能记住本文中的一行内容,请记住这一行:

curl -sSL https://example.com

解读响应

初学者常常只盯着响应正文,却忽略了答案其实就在请求头中。

curl -i https://example.com
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256

第一行是状态码。200

表示操作成功。301

和 302

表示重定向——需添加 -L

。401

和 403

表示访问被拒绝。404

表示该资源不存在。429

表示请求速度过快。500

及以上状态码表示服务器出现问题。

content-type

这能告诉你实际收到了什么,并能消除许多困惑。如果你调用了一个 API 并期望收到 JSON 数据,却看到 text/html

,说明你收到的是错误页面或登录重定向,而你即将遇到的解析错误只是症状,而非根本原因。

若要获取不包含正文的请求头:

curl -sS -o /dev/null -D - https://example.com

该命令执行常规的 GET 请求,忽略正文内容,并输出请求头。这比 -I

更可靠,后者会发送 HEAD

请求,行为可能有所不同——关于这一区别,请参阅我们的 curl HEAD 请求指南。

若需摘要而非原始标头,-w

会打印选定的值:

curl -sS -o /dev/null -w 'status=%{response_code} time=%{time_total}s\n' https://example.com

关于如何正确读取标头的更多内容,请参阅 使用 curl 显示响应标头。

发送数据

这是任务的另一半。

包含表单数据的 POST 请求:

curl -d "name=Ada&role=engineer" https://api.example.com/users

使用 -d 默认采用 POST 方法,并设置 Content-Type: application/x-www-form-urlencoded。

包含 JSON 的 POST 请求 —— 这是初学者最常犯的一个错误,因为仅使用 -d 并不会设置 JSON 内容类型:

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Ada","role":"engineer"}'

若忽略该头部,许多 API 会返回 415 Unsupported Media Type 错误,这会让人感到困惑,直到你明白它指的是你的 Content-Type 而不是你的数据。我们曾在 什么是 415 状态码 中专门讨论过这个错误。

来自文件的数据,使用 @ 表示“读取此文件”:

curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d @payload.json

包含查询参数的 GET 请求,由键值对构成,使用 -G:

curl -G https://api.example.com/search -d "q=proxy" -d "limit=10"

其他方法使用 -X。仅将其用于没有专用选项的方法——PUT、DELETE、PATCH。请注意手册中的警告:-X“仅更改 HTTP 请求中实际使用的词语,不会改变 curl 的行为方式”,这就是为什么 -X HEAD 无法正常工作,而 -I 却存在的原因。

请求头、身份验证和Cookie

使用 -H

设置 自定义请求头,可重复操作:

curl -H "Authorization: Bearer eyJhbG..." \
     -H "Accept: application/json" \
     https://api.example.com/me

使用 -u

进行 基本身份验证:

curl -u username:password https://api.example.com/private

省略密码,curl 会提示输入,这样可以避免密码出现在 shell 历史记录中:

curl -u username https://api.example.com/private

使用 -A

设置 用户代理,因为 curl 默认会将自身标识为 curl,而某些服务器对此会做出不同的响应:

curl -A "Mozilla/5.0 (compatible; MyBot/1.0; +https://example.com/bot)" https://example.com

如果您正在编写自动化客户端,使用包含联系 URL 的真实用户代理既是一种礼貌,也具有实际优势——匿名自动化被拦截的概率远高于已标识的自动化。

**Cookie。**除非你主动要求,否则 curl 不会在不同调用之间保留 Cookie:

curl -c cookies.txt -d "user=ada&pass=secret" https://example.com/login
curl -b cookies.txt https://example.com/dashboard

-c

用于写入 Cookie,-b

用于读取 Cookie。这就是处理任何需要会话的情况的方法。

查看实际传输的内容

区分“快速调试者”与“凭空猜测者”的关键习惯。

curl -v https://example.com

手册中对前缀的解释如下:“>

表示 curl 发送的头部,<

表示 curl 接收的头部,}

表示 curl 发送的数据,{

表示 curl 接收的数据,*

表示 curl 提供的附加信息。”

若仅需查看您发送的内容:

curl -v https://example.com 2>&1 | grep '^>'

这可以消除一类常见的困惑,因为您在代码中设置的头部信息并不总是与实际传输的头部信息一致。库会添加默认值、覆盖现有值并重新排序。当服务器“忽略”您的头部信息时,请先检查您是否确实发送了该头部。

详细输出会发送到 stderr,这就是为什么在管道传输前需要添加 2>&1

—— 这是有意为之,以确保 stdout 上的正文保持干净。

手册中给出的一个警告值得重申:详细输出和跟踪输出“可能包含敏感数据,包括用户名、凭据或机密数据内容”。在粘贴到工单之前请进行屏蔽处理。

值得记住的标志

以上内容可归纳为一小组标志。

标志作用
-i显示包含响应正文的响应头
-o file / -O保存到指定文件 / 远程名称
-L跟随重定向
-sS静默模式,但仍报告错误
-H添加请求头
-d发送数据(默认POST请求)
-u基本认证
-v显示完整交互记录
--fail将HTTP错误视为传输失败
-m / --connect-timeout超时设置

最后两项是初学者常忽略、日后却会后悔的设置。

--fail 之所以重要,是因为 curl 默认将 404 视为传输成功——它会下载错误页面并以 0 退出。在脚本中,这意味着你会将 HTML 错误页面保存为 installer.dmg,然后继续执行。--fail 则会让 HTTP 错误产生非零退出代码且不输出任何内容。

超时很重要,因为 curl 默认没有总体时间限制。一个挂起的请求会导致脚本无限期挂起。--connect-timeout 5 -m 30 可以对此进行限制。更多相关内容请参见 使用 curl 设置超时。

值得在每个脚本中添加的一行代码:

curl --fail --silent --show-error --location --connect-timeout 5 --max-time 30 "$URL"

一个从头到尾的示例

通过一个实际任务将各个部分整合起来:调用公共 API、检查是否成功,以及处理失败情况。

第一步——查看端点返回的内容。 先从请求头开始,而不是请求体:

curl -sS -o /dev/null -D - https://api.github.com/repos/curl/curl

你会收到状态行和请求头。如果状态码为 200

,且 content-type

显示为 JSON,说明你调用的是正确的接口。

第二步 — 查看格式化的请求正文。 单行原始 JSON 难以阅读,因此将其通过 jq

进行处理:

curl -sS https://api.github.com/repos/curl/curl | jq '{name, stargazers_count, language}'

如果未安装 jq

,python3 -m json.tool

可以在无需额外依赖的情况下完成格式化工作。

**第三步——检查你发送的内容。**如果出现异常,请查看请求内容,而不是凭空猜测:

curl -v https://api.github.com/repos/curl/curl 2>&1 | grep '^>'

**第四步——确保脚本安全运行。**添加错误处理和超时机制,并将状态码与请求主体分开处理:

#!/usr/bin/env bash
set -euo pipefail

URL="https://api.github.com/repos/curl/curl"
BODY=$(mktemp)

STATUS=$(curl --silent --show-error --location \
              --connect-timeout 5 --max-time 30 \
              --write-out '%{response_code}' --output "$BODY" \
              "$URL")

case "$STATUS" in
  200) jq -r '.stargazers_count' < "$BODY" ;;
  404) echo "not found" >&2; exit 1 ;;
  429) echo "rate limited, retry after: $(date)" >&2; exit 1 ;;
  *)   echo "unexpected status $STATUS" >&2; head -c 200 "$BODY" >&2; exit 1 ;;
esac

rm -f "$BODY"

其中有三点值得应用到你编写的每一行代码中。--write-out '%{response_code}'

配合 --output

可将状态码与请求体分离,以便你根据状态码进行分支处理。当遇到意外状态码时,打印请求体的前 200 个字符,能将难以理解的错误转化为易于阅读的错误信息。而 --connect-timeout

配合 --max-time

则意味着,即使网络连接中断,脚本仍会正常结束执行。

第五步——遵守速率限制。 公共 API 会在请求头中公布其限制。读取这些信息无需任何成本,且能避免最常见的被封禁情况:

curl -sS -o /dev/null -D - https://api.github.com/repos/curl/curl | grep -i ratelimit

初学者常见的错误

忘记设置-L。 你会收到一条包含重定向提示的简短响应,并因此认为该URL无法访问。其实并非如此。

在脚本中忘记设置--fail。 404 会变成一个已保存的错误页面,并返回退出代码0。这种情况不会引发明显错误提示,但日后会造成巨大损失。

在处理 JSON 时使用 -d 却未设置 Content-Type。 这会在服务器端产生 415 或令人困惑的解析错误。

Shell 引号处理。 单引号会原样保留所有内容;双引号会让 Shell 展开 $ 以及反引号。对于包含双引号的 JSON 主体,请用单引号将其包裹起来。 如果您的数据中还包含单引号,请将其写入文件并使用 -d @file.json。

假设 curl 看到的与浏览器看到的一样。 curl 不执行 JavaScript。如果某个页面在浏览器中看起来内容完整,但 curl 返回的响应却几乎为空,这意味着内容是在客户端渲染的,而 curl 的行为是正确的。

忽略状态码。 响应体显示“error”但状态码为 200 的情况,与响应体显示“error”但状态码为 500 的情况,属于不同的问题。请同时查看这两者。

在命令行中输入凭据。 这些凭据会被记录在 shell 历史记录中,并且该机器上的其他用户可以在进程列表中看到它们。 请使用 -u user,让 curl 进行提示,或者从环境变量中读取凭据。

为使某项功能正常运行而禁用证书验证。 -k 会屏蔽一条本应提醒你的警告。请先弄清楚警告的具体含义。

下一步该做什么

掌握基础知识后,接下来的自然步骤是:

正确下载 — 恢复中断的传输、并行下载、速率限制。相关内容详见 使用 curl 下载文件。

超时与重试,这正是让脚本不再脆弱的关键。 参见 使用 curl 设置超时。

将头部信息作为数据读取,使用 %{header_json} 可以获得 JSON 格式输出,而非需要解析的纯文本。

curl 与 wget,由于两者功能重叠且各有所长——curl 与 wget 的对比 介绍了何时该使用哪一个。

代理服务器,当你最终需要使用时:-x http://host:port 会将请求通过代理转发。对于地理位置验证和流量分流确实很有用;但对于学习或少量使用而言,确实没有必要。

手册。 man curl 篇幅较长,是权威的参考资料。阅读你已经使用的参数条目,是发现你真正想要的选项的可靠方法。

大家还问

curl 有什么用途?

通过命令行或脚本经由 URL 传输数据——调用 API、检查服务器返回的内容、下载文件,以及在应用程序外部重现请求以定位问题。它支持多种协议,但绝大多数情况下用于 HTTP 和 HTTPS。

如何使用 curl 发送 GET 请求?

curl https://example.com. GET 是默认方法,因此无需指定参数。添加 -L 可跟随重定向,添加 -i 可在查看请求主体的同时查看响应头。

如何使用 curl 发送 JSON?

curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' URL。请求头至关重要——仅使用 -d 会发送表单编码的内容类型,而期望接收 JSON 的 API 通常会返回 415 错误并拒绝该请求。

为什么 curl 没有返回任何内容?

可能有几种原因:响应正文确实为空;您跟随了一个未实际执行的重定向(请添加 -L);内容由 JavaScript 渲染,而 curl 不会执行 JavaScript;或者请求失败,但由于使用了 -s 却未添加 -S,导致您未看到错误信息。请使用 -i 运行命令以查看状态码。

curl 中 -o 和 -O 有什么区别?

-o filename 会保存为自选的文件名。-O 会根据 URL 中的文件名进行保存,并忽略路径。当 URL 中没有有用的文件名,或者需要指定特定文件名时,请使用 -o。

如何查看 curl 发送的请求?

运行 curl -v URL,查找以 > 开头的行。详细输出会发送到 stderr,因此请在管道传输前添加 2>&1。这是确认您配置的头部信息是否已实际发送到网络上的最快方法。

curl 默认会跟随重定向吗?

不会。请添加 -L。这是初学者使用 curl 命令时常遇到短于预期的响应的最常见原因——你看到的是重定向,而非目标页面。

使用 curl 需要代理吗?

不需要。curl 通过您自己的网络连接访问公共端点时完全正常。只有当您的请求量大到触发速率限制,或者需要查看网站在其他国家/地区提供的内容时,代理才变得重要。在学习阶段,这两种情况都不需要额外购买任何服务。

总结

curl 的选项多得令人望而生畏,但真正有用的核心功能却非常有限。使用 -i 查看请求头,-L 跟随重定向,-o 保存内容,-H 添加请求头,-d 发送数据,-u 进行身份验证,-v 查看执行结果,以及 --fail 配合超时设置用于任何无人值守的任务。对于大多数人来说,这就是全部的常用功能集。

有两个习惯比任何标志都重要:在读取正文之前先查看状态码和 Content-Type,因为它们通常会直接指出问题所在;以及使用 -v 来检查你实际发送的内容,而不是你本意要发送的内容,因为这两者之间的差异正是许多意想不到的错误的根源所在。

除此之外的内容都属于手册范畴,手册篇幅较长、权威性强,每当你需要编写临时解决方案时,都值得翻阅一下。你想要的选项通常都存在。