HTTP 请求头是随每次请求和响应传输的元数据:它们告知服务器调用方是谁、消息体的内容类型、需要验证哪些凭据,以及期望返回的语言或格式。当你想要使用 cURL 发送 HTTP 请求头时,整个工作归结为一个标志,但围绕它的细节(多个请求头、覆盖默认值、删除某个请求头以及读取响应)才是大多数人遇到困难的地方。

本指南从一名实践工程师的角度梳理实际操作机制。你将学习 -H / --header 语法、如何发送单个或多个请求头、实际工作中最重要的几个请求头(User-Agent、Accept、Authorization、Cookie 等)、如何覆盖或删除默认请求头,以及如何查看服务器返回的内容。最后,我们将其与爬取场景关联起来:真实的请求头可以让请求看起来像浏览器发出的,但这只是一半,因此我们还将展示如何在请求头不够用时通过住宅代理路由 cURL。

-H 标志:基本语法

cURL 使用 -H 标志(或其完整形式 --header)发送自定义请求头,后跟请求头名称、冒号和值,整体用引号括起来,以便 shell 将其视为一个参数。

bash
curl -H "Header-Name: value" https://example.com

这就是完整的模式。引号很重要:请求头的值通常包含空格,不加引号 shell 会将值拆分为多个参数,cURL 会拒绝执行。根据 HTTP 规范,请求头名称不区分大小写,但最好使用规范形式(User-Agent 而非 user-agent),这样命令更易读,也与服务器日志保持一致。

如果你更喜欢完整的长格式,--header 与之完全相同。在脚本中使用哪种更易读取决于个人习惯;大多数人在命令行中使用 -H,在提交到代码库的脚本中则写全称。

发送单个请求头

最常见的单个请求头是 User-Agent。cURL 会发送自己默认的 User-Agent(类似 curl/8.4.0),任何服务器一眼就能看出你不是浏览器。覆盖它只需一个标志。

bash
curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" https://example.com

现在服务器看到的是当前的 Chrome 字符串,而非 cURL 标识符。同样的单请求头模式适用于任何其他字段。要从 API 请求 JSON 而非服务器默认格式,设置 Accept

bash
curl -H "Accept: application/json" https://api.example.com/data

发送多个请求头

实际请求很少只携带一个请求头。要发送多个,每个请求头重复一次 -H。顺序会保留,cURL 会严格按照你书写的顺序发送每一行。

bash
curl -H "Accept: application/json" \
     -H "Accept-Language: en-US,en;q=0.9" \
     -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     https://api.example.com/data

反斜杠允许你将命令跨行书写以提高可读性;写在一行效果相同。-H 标志的数量没有实际限制,因此一个模仿浏览器握手的请求可以携带十几个请求头,无需任何特殊处理。

最重要的几个请求头

在 API 工作和爬取场景中,少数几个请求头承担了主要工作。以下是每个请求头的用途及设置方式。

User-Agent

标识客户端。设置一个真实、最新的浏览器字符串,让服务器将请求视为普通流量。过时或明显合成的 User-Agent 是常见的封锁触发因素,请保持更新。

Accept 与 Accept-Language

告知服务器你能处理的内容类型和语言。浏览器始终会发送这两个请求头;裸 cURL 请求通常不会,这本身就是一个指纹特征。发送合理的值可以填补这一缺口。

bash
curl -H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
     -H "Accept-Language: en-US,en;q=0.9" \
     https://example.com

Referer

表示链接到当前页面的来源页面。许多网站在内部导航时期望同源 Referer,若缺失则视为可疑行为。将其设置为该网站自身域名(或你本应来自的页面),可以让请求更可信。

bash
curl -H "Referer: https://example.com/" https://example.com/products

Content-Type

声明你发送的消息体格式。它仅在有请求体时才适用,因此出现在 POST 和 PUT 请求中,而非普通的 GET 请求。发送 JSON 时,服务器需要知道将其解析为 JSON。

Authorization

携带凭据。有两种主要形式。Bearer token(OAuth、API 密钥)直接放入请求头:

bash
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://api.example.com/me

对于 HTTP Basic 认证,你可以手动构建请求头,但 cURL 提供了快捷方式。-u 标志会将 user:pass 进行 base64 编码并生成 Authorization: Basic 请求头,比自行编码更不容易出错。

bash
# cURL builds the Basic header for you
curl -u myuser:mypassword https://api.example.com/secure

# Equivalent, written out by hand
curl -H "Authorization: Basic bXl1c2VyOm15cGFzc3dvcmQ=" https://api.example.com/secure

发送已存储的会话状态。你可以通过 -H 内联传入 Cookie 用于一次性请求,或使用 cURL 的 -b 标志,它也接受由之前 -c 调用写入的 Cookie 文件,使会话在多次请求间持续有效。

bash
curl -H "Cookie: session_id=abc123; theme=dark" https://example.com/account
请求头名称不区分大小写,值区分

HTTP 对请求头名称不区分大小写,因此 User-Agentuser-agent 指向同一字段。而值则原样发送。token 中的拼写错误或 User-Agent 中多余的空格都会被原封不动地传递,因此当请求行为异常时,先逐字节检查值,再归咎于服务器。

覆盖与新增请求头的区别

cURL 会自动发送少数几个请求头:HostUser-AgentAccept 等。当你为 cURL 已发送的请求头传入 -H 时,你的值会替换默认值,而不是追加第二个副本。这就是为什么 -H "User-Agent: ..." 能干净地替换 cURL 字符串,而不会产生两行 User-Agent。

对于 cURL 默认不发送的请求头,你的 -H 只是新增它。因此逻辑模型很直观:如果是默认请求头则覆盖,否则就是新增。无论哪种情况,语法都是同一个标志。

删除默认请求头

有时你希望完全删除某个请求头,而不是替换它。要删除默认请求头,在其名称后加上冒号但不跟任何值。cURL 将空值解读为从请求中省略该请求头的信号。

bash
# Remove the Accept header entirely
curl -H "Accept:" https://example.com

# Override one default and remove another in the same request
curl -H "User-Agent: Mozilla/5.0" -H "Accept:" https://example.com

注意区别:"Accept: value" 设置请求头,"Accept:" 不带值则删除它,而末尾加分号("Accept;")则是发送一个空值的请求头,这是第三种更少见的情况。大多数时候你只需要前两种。

查看响应头

发送请求头是对话的一半,读取返回内容是另一半。cURL 提供了三种选项,取决于你是否也需要响应体。

使用 -I(或 --head)通过 HEAD 请求仅获取响应头,不下载响应体:

bash
curl -I https://example.com

使用 -i(或 --include)在完整响应体前打印响应头,适合同时查看两者:

bash
curl -i https://example.com

使用 -D(或 --dump-header)将响应头写入文件,同时将响应体输出到标准输出。传入 -D - 可将响应头与响应体一起输出到标准输出,在需要保留日志时很有用。

bash
# Save response headers to a file, body to stdout
curl -D headers.txt https://example.com

典型的响应头块如下所示,一眼就能看出状态码、内容类型和缓存策略:

bash
HTTP/2 200
content-type: text/html; charset=UTF-8
cache-control: max-age=3600
server: nginx

在 POST 请求中发送自定义请求头

请求头在写入请求中更能发挥作用。向 API 发送典型的 JSON POST 请求,至少需要一个 Content-Type 让服务器正确解析请求体,通常还需要 Authorization 请求头。-d 标志提供请求体,cURL 在看到请求体时会自动切换为 POST。

bash
curl -X POST https://api.example.com/items \
     -H "Content-Type: application/json" \
     -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     -d '{"name": "widget", "qty": 12}'

一旦传入 -d-X POST 在技术上是多余的,但在脚本中明确写出来意图更清晰。如果服务器期望表单数据而非 JSON,将 Content-Type 改为 application/x-www-form-urlencoded 并以相同方式传入字段即可。

如果你正在了解请求基础知识,我们关于如何使用 cURL 发送 GET 请求的教程涵盖了同一工具集的读取端。

请求头与网络爬取:能做什么,做不到什么

这是请求头知识与现实碰撞的地方。默认 cURL 安装发出的请求与浏览器请求显而易见地不同:它发送 curl/x.y.z User-Agent,不包含 Accept-Language,没有 Referer,也没有真实浏览器包含的任何客户端提示。即使具备基本防护的服务器也会精准地盯住这些差距。

设置真实的请求头可以填补这些明显缺口。当前的 Chrome User-Agent、合理的 AcceptAccept-Language,再加上同源 Referer,合在一起能让你的请求看起来更像普通的浏览器流量。对于防护较弱的页面,这通常足以停止被拦截。我们关于 cURL 网络爬取的指南深入介绍了如何构建可信的请求头集合。

但请求头有其上限。它们描述请求,但对请求来源毫无说明。反爬虫系统还会评估 IP 信誉、请求频率、TLS 指纹和行为信号。如果你从单个数据中心 IP 每分钟发出一百个请求,再好的请求头组合也无济于事:IP 地址本身才是暴露点。真实的请求头能减少简单拦截,但单靠它们无法突破严密的反爬虫防御或糟糕的 IP 信誉。

通过住宅代理路由 cURL

缺失的另一半是 IP。当你将 cURL 路由通过一批轮换的住宅地址时,你的请求来自被视为真实用户的 IP,同时你仍对每个请求头保持完全控制。cURL 已经通过 -x(或 --proxy)标志原生支持与代理通信,因此添加代理只是在已有请求头基础上增加一个参数。

Smart AI Proxy(也称为 AI Proxy)将住宅轮换作为单一直连端点提供。你用 -x 将 cURL 指向它,用 token 进行认证,发送任何你想要的请求头;代理在后台轮换出口 IP。

bash
curl -x "http://[email protected]:8012" \
     -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
     -H "Accept-Language: en-US,en;q=0.9" \
     -k https://example.com

-x 标志设置代理,-H 标志与之前一样携带你的真实请求头,请求从可信的住宅 IP 发出。你保留了 cURL 赋予你的完整请求头控制权,并获得了请求头单独无法提供的 IP 信誉。关于代理任何 cURL 请求的通用机制,请参阅如何使用 cURL 配合代理

Crawlbase Smart AI Proxy

继续在 cURL 中自定义请求头,让 IP 问题自动解决。Smart AI Proxy 通过一个端点轮换住宅地址,让你精心设置的 User-Agent、Accept-Language 和 Referer 从服务器信任的 IP 发出。只需一个 -x 标志将 cURL 指向它,即可完全控制每一个请求头。

如果你想了解像这种正向代理与位于服务器前的反向代理之间的区别,我们关于正向代理与反向代理的文章划清了界限。

调试请求头问题

当请求行为出乎意料时,最快的工具是 -v(详细模式),它会打印 cURL 实际发送的请求头(前缀为 >)和接收到的响应头(前缀为 <)。它精确呈现了通过网络传输的内容,通常足以发现缺失的请求头或拼写错误。

bash
curl -v -H "Authorization: Bearer YOUR_ACCESS_TOKEN" https://api.example.com/me

三类问题占了大多数请求头 bug 的原因。第一,缺少引号:包含空格的值会被拆分成额外的 shell 参数,导致 cURL 报错,因此始终将整个 name: value 字符串加引号。第二,意外覆盖:传入 -H "User-Agent: ..." 会替换默认值而非追加,对 User-Agent 来说这正是期望行为,但如果你以为两者都会存在则会出乎意料。第三,意外删除:多余的 "Header:" 不带值会完全删除该请求头。运行 -v,真相就在请求行中。

回顾

核心要点

  • 一个标志搞定一切。 -H "Name: value" 发送任意请求头;重复 -H 可发送任意数量的请求头,顺序保留。
  • 覆盖、新增或删除。 针对默认请求头的 -H 会替换它,针对新请求头的 -H 会添加它,"Header:" 不带值则删除它。
  • 同样读取响应。 -I 仅获取响应头,-i 将其置于响应体前,-D 将其写入文件或标准输出。
  • 真实的请求头减少简单拦截。 真实的 User-Agent、Accept-Language 和 Referer 让请求看起来像浏览器发出的,但无法突破严密的反爬虫防御。
  • 将请求头与可信 IP 配合使用。 通过 -x 将 cURL 路由至 Smart AI Proxy,使请求从轮换的住宅地址发出,同时保持完整的请求头控制权。

常见问题

如何在 cURL 中添加自定义 HTTP 请求头?

使用 -H 标志(或其完整形式 --header),后跟请求头名称、冒号和值,整体加引号:curl -H "Header-Name: value" https://example.com。引号防止 shell 将包含空格的值拆分成多个参数。

如何在一次 cURL 请求中发送多个请求头?

每个请求头重复一次 -H 标志。例如,curl -H "Accept: application/json" -H "Authorization: Bearer TOKEN" https://api.example.com 会同时发送两个。数量没有实际限制,cURL 会按你书写的顺序保留。

如何使用 cURL 设置 Bearer token 或 Basic 认证?

对于 Bearer token,直接传入:-H "Authorization: Bearer YOUR_TOKEN"。对于 HTTP Basic 认证,-u user:pass 标志比手动构建请求头更简便,因为 cURL 会自动对凭据进行 base64 编码并设置 Authorization: Basic 请求头。

如何使用 cURL 仅查看响应头?

使用 -I(或 --head)通过 HEAD 请求仅获取响应头,跳过响应体。若要同时获取响应头和响应体,使用 -i(或 --include)。要将响应头保存到文件,使用 -D filename

如何删除 cURL 发送的默认请求头?

在请求头名称后加冒号但不跟值:curl -H "Accept:" https://example.com 会完全删除 Accept 请求头。这与 "Accept: value"(设置值)不同,也与 "Accept;"(发送空值)不同。

自定义请求头能阻止我的爬虫被封锁吗?

真实的请求头对防护较弱的网站有帮助,可以让请求看起来像浏览器发出的,但单靠它们无法突破严密的反爬虫系统或糟糕的 IP 信誉。请求头描述请求,而非请求的来源。将其与轮换住宅 IP 配合使用,例如通过 -x 标志将 cURL 路由至 Smart AI Proxy,让流量从可信地址发出,同时保持完整的请求头控制权。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量