CapMonster Cloud 的 Header 顺序:如何复现浏览器请求头并绕过反机器人保护

在自己的网站上测试保护系统并使用 CapMonster Cloud 服务时,许多开发者都会遇到这样一种情况:CAPTCHA 已成功求解,令牌也已获取——但服务器仍然拒绝该请求。
当你实现 CAPTCHA 或反机器人机制时,你需要准确理解服务器是如何作出判断的——以及它使用哪些信号来识别自动化行为。问题通常不在于 CAPTCHA 本身,而在于 HTTP 请求是如何发送的。
其中一个关键因素是请求头顺序。在本文中,你将了解为什么顺序很重要、如何正确复现它,以及如何将其与 CapMonster Cloud 集成。
什么是请求头顺序
一个 HTTP 请求不仅由 URL 和请求体组成,还包含一组请求头:
User-Agent
Accept
Accept-Language
Cookie
...服务器不仅会读取请求头的内容——它还会看到它们的顺序。
为什么这对反机器人保护很重要
现代保护系统(Cloudflare、DataDome、Imperva 等)会分析:
- TLS 指纹(JA3/JA3S)
- HTTP/2 SETTINGS 指纹(Akamai H2 fingerprint)
- User-Agent
- IP 声誉(ASN、数据中心 IP 或住宅 IP)
- JavaScript 指纹
- 请求头顺序
- 客户端行为
真实浏览器(Chrome、Firefox 等)发送请求头时会:
- 按照严格定义的顺序发送
- 在每次请求中都保持一致
但常见的 HTTP 库——requests(Python)和 axios(Node.js)——可能会在用户定义的请求头之外添加默认请求头(User-Agent, Accept-Encoding, Connection),对请求头进行排序,或以随机顺序发送它们,这会破坏预期的顺序并暴露自动化特征。
这与 CapMonster Cloud 有什么关系
CapMonster Cloud 只负责求解 CAPTCHA,并不会让你的请求“看起来像浏览器发出的”。
也就是说:
- 你收到了一个有效的 token
- 把它添加到了请求中
- 但服务器仍然返回错误
为什么?因为你的请求看起来像一个机器人。
浏览器如何发送请求头(Chrome 146*)
在真实浏览器中,这种顺序几乎每次都会重复出现:
* 在撰写本文时,Chrome 146 是当前版本。
HTTP/2 伪请求头
:method
:authority
:scheme
:path常规请求头(Chrome 示例)
sec-ch-ua
sec-ch-ua-mobile
sec-ch-ua-platform
upgrade-insecure-requests
user-agent
accept
sec-fetch-site
sec-fetch-mode
sec-fetch-user
sec-fetch-dest
accept-encoding
accept-language
priority一个常见错误——从 DevTools 复制
很多开发者会这样做:
- 打开 Chrome DevTools
- 复制请求头
- 将它们粘贴到代码中
问题在于:
- DevTools 并不总是显示真实顺序,而且可能会对请求头进行排序
- 因此,代码中的顺序会与浏览器中的顺序不同
如何获取正确的顺序
使用能够显示真实请求的工具:
- Charles Proxy
- mitmproxy
- powhttp
它们显示的请求头顺序与服务器看到的完全一致。
例如(使用 Charles Proxy 抓取):
:method: GET
:authority: example
:scheme: https
:path: /assets/js/example.js
sec-ch-ua-full-version-list: "Chromium";v="146.0.7680.178", "Not-A.Brand";v="24.0.0.0", "Google Chrome";v="146.0.7680.178"
sec-ch-ua-platform: "Windows"
sec-ch-ua: "Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"
sec-ch-ua-bitness: "64"
sec-ch-ua-model: ""
sec-ch-ua-mobile: ?0
sec-ch-ua-arch: "x86"
sec-ch-ua-full-version: "146.0.7680.178"
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
sec-ch-ua-platform-version: "19.0.0"
accept: */*
sec-fetch-site: same-origin
sec-fetch-mode: no-cors
sec-fetch-dest: script
referer: https://example.com/
accept-encoding: gzip, deflate, br, zstd
accept-language: en-US,en;q=0.9,ru;q=0.8
priority u=0, i如何在代码中复现请求头顺序
一旦你拿到了真实的请求头顺序,下一步任务就是在 HTTP 客户端中正确复现它。
关键问题在于,大多数标准库都不保证请求头顺序。
为了准确复现浏览器行为,建议使用专门的解决方案:
- tls-client(Go / Python)——通过 Go 绑定层的 header_order + pseudo_header_order 实现请求头顺序控制 bogdanfinn/tls-client,并将这些参数直接传递给原生库
- 自定义 HTTP/2 客户端
- 浏览器自动化(例如 Playwright)——如果可以接受其额外开销
配置示例(Python + tls-client)
下面的示例演示了复现类似 Chrome 的请求头顺序的一种基础方法。
重要提示:chrome_146 是 Go 库 bogdanfinn/tls-client 中的有效 profile,文档中标注为 “Latest”。不过,标准 Python 包 FlorianREGAZ/Python-Tls-Client(pip install tls-client)已经停止维护,且只包含到 chrome_120 为止的 profile。读者如果安装标准包,将会收到错误。请使用当前的 bogdanfinn/tls-client 分支,或使用支持 chrome_146 的兼容 Python 绑定。
import tls_client
session = tls_client.Session(
client_identifier="chrome_146",
random_tls_extension_order=True
)
session.pseudo_header_order = [
":method",
":authority",
":scheme",
":path",
]
session.header_order = [
"sec-ch-ua",
"sec-ch-ua-mobile",
"sec-ch-ua-platform",
"upgrade-insecure-requests",
"user-agent",
"accept",
"sec-fetch-site",
"sec-fetch-mode",
"sec-fetch-user",
"sec-fetch-dest",
"accept-encoding",
"accept-language",
"cookie",
"priority",
]
headers = {
"sec-ch-ua": '"Chromium";v="146", "Not-A.Brand";v="24", "Google Chrome";v="146"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/146.0.0.0 Safari/537.36",
"accept": "*/*",
"sec-fetch-site": "same-origin",
"sec-fetch-mode": "no-cors",
"sec-fetch-dest": "script",
"accept-encoding": "gzip, deflate, br, zstd",
"accept-language": "en-US,en;q=0.9",
"priority": "u=0, i",
}
response = session.get("https://example.com", headers=headers)
print(response.status_code)session.header_order 中的顺序必须与您从真实浏览器中获取到的顺序一致。任何偏差都可能影响结果。
HTTP/2 SETTINGS 指纹
反机器人系统分析的不仅仅是请求头——它们甚至会在解析请求头之前检查 HTTP/2 SETTINGS 指纹。Cloudflare 和 DataDome 使用所谓的 Akamai H2 fingerprint,它会将各项参数编码为如下格式的字符串:
SETTINGS|WINDOW_UPDATE|PRIORITY|PSEUDO_HEADER_ORDER
这种信号可以在不分析请求内容的情况下,立即识别出 curl、requests 和 axios。tls-client 通过 client_identifier 自动解决了这个问题:它会为所选浏览器应用正确的 H2 SETTINGS profile。这就是为什么参数 client_identifier="chrome_146" 不只是一个“Chrome 版本”,而是整个网络栈的完整 profile。
CAPTCHA 求解后 Cookie 的作用
在成功通过 CAPTCHA 后,服务器通常还会设置额外的 Cookie。根据所使用的保护系统不同,这些 Cookie 可能包括:
- cf_clearance(Cloudflare)
- datadome(DataDome)
- visid_incap(Imperva)
这些 Cookie 会成为后续会话的一部分,并且必须在之后的请求中一并发送。
cookie 请求头必须出现在 Header Order 中的正确位置。如果它:
- 没有包含在请求头顺序列表中
- 或被自动追加到末尾
这都可能破坏预期的请求结构,并导致请求被拒绝。
在 tls-client 中通过 session 传递 Cookie 的示例:
import tls_client
session = tls_client.Session(
client_identifier="chrome_146",
random_tls_extension_order=True,
)
# 在从 CapMonster Cloud / 手动获取 cf_clearance 后 —— 将其传入 session
session.cookies.set(
"cf_clearance",
"received_value",
domain=".example.com",
)
# 后续所有请求都会自动在正确位置包含该 cookie
response = session.get(
"https://example.com/protected-page",
headers=headers,
)
print(response.status_code)影响结果的其他错误
请求之间的请求头不一致
真实浏览器会使用稳定的一组请求头。如果你的脚本:
- 在不同阶段发送不同的请求头
- 改变它们的顺序
- 无规律地添加或删除请求头
这会被视为异常。
Referer 不正确
referer 请求头在导航上下文中起着重要作用。它缺失或不匹配都可能显得可疑。
例如,如果你请求的是某个通常从页面中加载的资源,却没有指定 referer,这可能会触发额外检查。
sec-fetch 请求头不匹配
Sec-Fetch-Site、Sec-Fetch-Mode 和 Sec-Fetch-Dest 请求头必须与真实的请求上下文一致。Sec-Fetch-Site 表示发起方与目标资源之间的关系,Sec-Fetch-Mode 表示请求模式,而 Sec-Fetch-Dest 表示所加载资源的目标类型。
错误或不一致的取值可能会与 HTTP 请求中的其他数据相矛盾,并被服务器用作拒绝请求的信号。错误在以下情况下尤其常见:对正常链接点击使用 none,或者将 cross-origin 与 cross-site 混淆。
根据请求类型对应的正确取值表:
使用 HTTP/1.1 而不是 HTTP/2
包括 Chrome 在内的现代浏览器默认使用 HTTP/2。
如果你的客户端通过 HTTP/1.1 工作,这本身就会造成明显差异。再结合其他因素,这可能会影响反机器人系统的判断。
硬编码 sec-ch-ua-full-version-list
sec-ch-ua-full-version-list 请求头只会在响应服务器返回的 Accept-CH 时出现(Client Hints API)——它不会自动出现在每个请求中。在“冷启动”请求中,它根本不应出现。
在代码中硬编码这个请求头,可能会导致 User-Agent、sec-ch-ua 与真实 TLS profile 之间不匹配。结果就是,请求看起来前后不一致,并可能被反机器人系统拒绝。
在某些情况下(例如处理 DataDome 时),更安全的做法是,要么从真实流量中获取它,要么在没有必要时不要手动设置它。
重复的 Cookie
另一个常见错误是发送多个同名但值不同的 Cookie。例如:

这种情况可能发生于:
- 在不使用 cookie jar 的情况下手动管理 Cookie
- 库错误地合并了 Cookie
- 值被重复设置
正确的做法是:
- 使用内置的 Cookie 管理机制(cookie jar)
- 在发送前检查最终的 cookie 请求头
- 避免重复的名称
实用的调试方法
诊断问题最可靠的方法,是将真实浏览器请求与你自己的请求进行对比。
推荐流程:
- 使用 Charles Proxy 或 mitmproxy 等工具从浏览器中抓取请求
- 从你的代码中执行相同的请求
- 逐行比较两者
需要特别关注:
- 请求头顺序
- 特定请求头是否存在
- 请求头取值
- Cookie
即使是很小的差异也可能很重要。
结论
CapMonster Cloud 服务可以帮助获取正确的 CAPTCHA 解答,但之后在你的应用中如何处理和使用该解答,仍然由你自己负责。
例如,Cloudflare Bot Management 会同时检查 TLS、H2 SETTINGS 和请求头——只要某一层失败,就足以触发拦截。要想成功与受保护系统交互,你需要:
- 在 HTTP 层面复现浏览器行为
- 保持精确的请求头顺序
- 通过 client_identifier 使用正确的 TLS profile 和 H2 SETTINGS
- 保持请求一致性
- 通过 cookie jar 正确处理 Cookie
只有将这些因素结合起来,才能在测试和处理反机器人保护时获得稳定结果。






