网站突然弹出一整页“正在检查您的浏览器……”,刷新也没用,这就是 Cloudflare Challenge——圈内也叫它“五秒盾”。它和 Cloudflare Turnstile 不是一回事:Turnstile 只是表单里的一个小部件,Challenge 会把整个页面拦下来,不通过验证就什么都拿不到。
CaptchaAI 用真实浏览器实例过这一关,返回一个 qa_session_cookie cookie,把它和对应的 User-Agent 一起带上,你的请求就能正常访问被保护的页面了。
在动手写代码前,先记住三条 Cloudflare Challenge 独有的硬性规则——这也是它和 CaptchaAI 支持的其他验证码类型最大的区别:
- 必须带代理 —— 求解器要用你自己的代理去解,这样
qa_session_cookie才会绑定到你能控制的 IP 上。 - User-Agent 也是返回结果的一部分 —— cookie 和一个具体的 User-Agent 字符串绑定,后续请求必须原样使用响应里给的那个 User-Agent,不能用你自己浏览器的。
- IP 和 User-Agent 必须全程一致 —— 之后访问受保护页面的每一次请求,代理和 User-Agent 都要和解题时完全相同,换一个都会被打回验证页。
下面给出 Python、Node.js、PHP 三套可直接跑起来的完整实现。
找的是 Cloudflare Turnstile? 那是另一种验证码,解法也不一样。看这篇:如何用 API 解决 Cloudflare Turnstile。
为什么用 CaptchaAI 处理 Cloudflare 挑战
自己搭浏览器集群硬啃这道全页拦截,投入产出比并不划算。CaptchaAI 值得用的原因:
- 真实浏览器求解 —— 用真实浏览器实例过 Cloudflare 的 JavaScript 验证,不是简单模拟,遇到升级也不容易失效。
- 一次返回你要的全部信息 ——
qa_session_cookie和对应的 User-Agent 在同一个响应里全给你,不用再拼凑。 - 支持多种代理协议 —— HTTP、HTTPS、SOCKS4、SOCKS5 都能用,接入你现有的代理池不用改造。
- 和其他类型统一的 API 模式 —— 提交 → 轮询 → 取结果,和 CaptchaAI 支持的所有其他验证码类型是同一套流程,学一次到处用。
- 按线程计费 —— 基于线程的套餐,最低 BASIC $15/月(5 线程)起,同一线程内解多少次都不额外收费。
本地化测试要点
在国内网络环境下测试 Cloudflare 保护的站点,有两点特别容易踩坑:
- 出口 IP 信誉分:跨境电商团队做 checkout 流程 QA 时经常遇到这个问题——测试用的 VPS 常开在中国香港或新加坡,公共云出口 IP 信誉分不如本地住宅网络,Cloudflare 的风控就更容易触发整页拦截。让求解器带上你自己控制、信誉分较高的代理去过验证,比反复更换测试节点省事得多。
- 合规边界:不管是用 API 自动过验证,还是人工点开浏览器,抓取的内容都应限定在你自己有权访问的范围内,符合《网络安全法》《数据安全法》与 PIPL(个人信息保护法)对数据处理的要求——这也是本文默认的适用场景。
开始前需要准备什么
准备好下面这些,再动手写代码:
- CaptchaAI API Key —— 在 captchaai.com/api.php 获取,32 位字符串。
- 目标页面 URL —— 被 Cloudflare 保护的那个页面的完整地址。
- 一个能用的代理 —— HTTP、HTTPS、SOCKS4 或 SOCKS5 都行,前提是这个代理能连到目标站点。
- 账号开通代理权限 —— CaptchaAI 账号默认不开代理功能,第一次请求前得先联系 CaptchaAI 支持开通。
- 运行环境 —— Python 3.7+ 配
requests,或 Node.js 18+(自带fetch)。
注意: 没开通代理权限,
method=cloudflare_challenge是用不了的。还没开通的话,先去 CaptchaAI 提工单。
如何判断你踩到的是 Cloudflare 挑战
Cloudflare 挑战是整页拦截,不是嵌入式小部件。出现下面任意一种情况,基本可以确定就是它:
- 内容加载前先弹出“正在检查您的浏览器……”或“请稍候……”。
- URL 一闪而过目标路径,紧接着就跳转到验证页。
- 响应头带
cf-mitigated: challenge,或者状态码是 403,附带一个 Cloudflare Ray ID。 - HTML 里能找到
<div id="challenge-body-text">,或者引用了/cdn-cgi/challenge-platform/。 - 通过验证后,浏览器里会出现一个
qa_session_cookiecookie。
如果你看到的是表单里嵌了一个带复选框或转圈动画的小部件,那不是 Challenge,是 Cloudflare Turnstile——解法完全不同,别用错方法。
Cloudflare 挑战和 Turnstile 到底差在哪
两种保护长得不像,解法也不一样,先分清楚再动手:
| Cloudflare 挑战 | Cloudflare Turnstile | |
|---|---|---|
| 长什么样 | 整页拦截插页(“正在检查您的浏览器……”) | 表单上一个小部件(复选框或转圈动画) |
| API 方法 | cloudflare_challenge |
turnstile |
| 要不要代理 | 要,强制 | 不用,可选 |
| 拿到什么 | qa_session_cookie cookie + User-Agent |
Turnstile token |
| 怎么用结果 | 每次请求都带上 cookie + User-Agent + 同一个代理 | 把 token 塞进表单字段,提交表单 |
| 和 IP 的关系 | cookie 绑定代理 IP | token 不绑定 IP |
| 常见场景 | 需要先过整页拦截,才能拿到页面上的公开内容 | 提交受保护的表单(登录、注册、结账) |
如果你面对的是表单里的小部件,用这篇 Turnstile 教程才对路。
求解流程
整个过程拆开看,其实就是提交、等待、轮询、拿结果四步:
Identify Cloudflare 验证流程 page
↓
POST to in.php
method=cloudflare_challenge
pageurl + proxy + proxytype
↓
receive captcha ID
↓
wait 20 seconds
↓
GET res.php (action=get, id=…, json=1)
↓ ↓
CAPCHA_NOT_READY status=1
(wait 5s, retry) ↓
extract qa_session_cookie + user_agent
↓
set cookie + User-Agent + same proxy
↓
access protected page
Python 实现
用 requests 就够了,不需要额外依赖:
import time
import requests
API_KEY = "YOUR_CAPTCHAAI_API_KEY"
PAGE_URL = "https://example.com/protected-page"
PROXY = "user:[email protected]:8080"
PROXY_TYPE = "HTTP"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_cloudflare_challenge(api_key, pageurl, proxy, proxytype):
"""Solve a Cloudflare 验证流程 and return qa_session_cookie cookie + User-Agent."""
# Step 1: Submit the task
submit_resp = requests.post(
SUBMIT_URL,
data={
"key": api_key,
"method": "cloudflare_challenge",
"pageurl": pageurl,
"proxy": proxy,
"proxytype": proxytype,
"json": 1,
},
timeout=30,
)
submit_resp.raise_for_status()
submit_data = submit_resp.json()
if submit_data.get("status") != 1:
raise RuntimeError(f"Submit failed: {submit_data}")
captcha_id = submit_data["request"]
print(f"Task created — captcha ID: {captcha_id}")
# Step 2: Wait before first poll (Cloudflare 验证流程 takes longer)
time.sleep(20)
# Step 3: Poll for result
for _ in range(60):
result_resp = requests.get(
RESULT_URL,
params={
"key": api_key,
"action": "get",
"id": captcha_id,
"json": 1,
},
timeout=30,
)
result_resp.raise_for_status()
result_data = result_resp.json()
if result_data.get("request") == "CAPCHA_NOT_READY":
time.sleep(5)
continue
if result_data.get("status") == 1:
return {
"qa_session_cookie": result_data["result"],
"user_agent": result_data["user_agent"],
}
raise RuntimeError(f"Polling error: {result_data}")
raise TimeoutError("Cloudflare 验证流程 solve timed out")
# Solve the challenge
solution = solve_cloudflare_challenge(API_KEY, PAGE_URL, PROXY, PROXY_TYPE)
print(f"qa_session_cookie: {solution['qa_session_cookie']}")
print(f"User-Agent: {solution['user_agent']}")
# Step 4: Access the protected page using the SAME proxy and User-Agent
session = requests.Session()
session.headers.update({"User-Agent": solution["user_agent"]})
session.cookies.set("qa_session_cookie", solution["qa_session_cookie"], domain="example.com")
proxies = {
"http": f"http://{PROXY}",
"https": f"http://{PROXY}",
}
response = session.get(PAGE_URL, proxies=proxies, timeout=30)
print(f"Status: {response.status_code}")
print(f"Content length: {len(response.text)} chars")
这段代码做了什么:
- 带上
method=cloudflare_challenge、页面 URL 和你的代理,向in.php提交任务。 - 先等 20 秒,再开始每 5 秒轮询一次
res.php。 - 拿到
qa_session_cookie的值和求解器实际用的 User-Agent 字符串。 - 用同一个代理、同一个 cookie、同一个 User-Agent 去请求受保护页面。
划重点:
qa_session_cookie同时绑定代理 IP 和 User-Agent,两者只要变一个,Cloudflare 就会拒绝请求,重新弹出验证页。
Node.js 实现
技术栈是 Node.js 的话,逻辑完全一样,直接照抄:
const API_KEY = "YOUR_CAPTCHAAI_API_KEY";
const PAGE_URL = "https://example.com/protected-page";
const PROXY = "user:[email protected]:8080";
const PROXY_TYPE = "HTTP";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function solveCloudflareChallenge(apiKey, pageurl, proxy, proxytype) {
// Step 1: Submit the task
const submitResp = await fetch(SUBMIT_URL, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
key: apiKey,
method: "cloudflare_challenge",
pageurl: pageurl,
proxy: proxy,
proxytype: proxytype,
json: "1",
}),
});
const submitData = await submitResp.json();
if (submitData.status !== 1) {
throw new Error(`Submit failed: ${JSON.stringify(submitData)}`);
}
const captchaId = submitData.request;
console.log(`Task created — captcha ID: ${captchaId}`);
// Step 2: Wait before first poll
await sleep(20_000);
// Step 3: Poll for result
for (let i = 0; i < 60; i++) {
const resultResp = await fetch(
`${RESULT_URL}?${new URLSearchParams({
key: apiKey,
action: "get",
id: captchaId,
json: "1",
})}`
);
const resultData = await resultResp.json();
if (resultData.request === "CAPCHA_NOT_READY") {
await sleep(5_000);
continue;
}
if (resultData.status === 1) {
return {
cfClearance: resultData.result,
userAgent: resultData.user_agent,
};
}
throw new Error(`Polling error: ${JSON.stringify(resultData)}`);
}
throw new Error("Cloudflare 验证流程 solve timed out");
}
(async () => {
const solution = await solveCloudflareChallenge(
API_KEY,
PAGE_URL,
PROXY,
PROXY_TYPE
);
console.log(`qa_session_cookie: ${solution.cfClearance}`);
console.log(`User-Agent: ${solution.userAgent}`);
// Step 4: Access protected page with cookie, User-Agent, and same proxy
// Note: Node.js fetch does not natively support proxies.
// Use a proxy agent library like undici, https-proxy-agent, or node-fetch with proxy.
// Example with undici:
//
// import { ProxyAgent } from 'undici';
// const proxyAgent = new ProxyAgent(`http://${PROXY}`);
//
// const response = await fetch(PAGE_URL, {
// headers: {
// 'User-Agent': solution.userAgent,
// 'Cookie': `qa_session_cookie=${solution.cfClearance}`,
// },
// dispatcher: proxyAgent,
// });
console.log("Use the qa_session_cookie cookie and User-Agent with the same proxy for all subsequent requests.");
})();
PHP 实现
用原生 curl 和 file_get_contents 就能写完,不需要额外的包:
<?php
$apiKey = "YOUR_CAPTCHAAI_API_KEY";
$pageUrl = "https://example.com/protected-page";
$proxy = "user:[email protected]:8080";
$proxyType = "HTTP";
// Step 1: Submit the task
$submit = file_get_contents("https://ocr.captchaai.com/in.php?" . http_build_query([
"key" => $apiKey,
"method" => "cloudflare_challenge",
"pageurl" => $pageUrl,
"proxy" => $proxy,
"proxytype" => $proxyType,
"json" => 1,
]));
$submitData = json_decode($submit, true);
if ($submitData["status"] !== 1) {
die("Submit failed: " . $submit);
}
$captchaId = $submitData["request"];
echo "Task created — captcha ID: $captchaId\n";
// Step 2: Wait and poll
sleep(20);
$cfClearance = null;
$userAgent = null;
for ($i = 0; $i < 60; $i++) {
$result = file_get_contents("https://ocr.captchaai.com/res.php?" . http_build_query([
"key" => $apiKey,
"action" => "get",
"id" => $captchaId,
"json" => 1,
]));
$resultData = json_decode($result, true);
if ($resultData["request"] === "CAPCHA_NOT_READY") {
sleep(5);
continue;
}
if ($resultData["status"] === 1) {
$cfClearance = $resultData["result"];
$userAgent = $resultData["user_agent"];
echo "qa_session_cookie: $cfClearance\n";
echo "User-Agent: $userAgent\n";
break;
}
die("Polling error: " . $result);
}
if (!$cfClearance) {
die("Solve timed out");
}
// Step 3: Access the protected page
$ch = curl_init($pageUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_PROXY => "123.123.123.123:8080",
CURLOPT_PROXYUSERPWD => "user:password",
CURLOPT_HTTPHEADER => ["User-Agent: $userAgent"],
CURLOPT_COOKIE => "qa_session_cookie=$cfClearance",
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "Status: $httpCode\n";
echo "Content length: " . strlen($response) . " chars\n";
常见错误与避坑指南
- 没带代理 —— API 直接拒绝请求。请求里补上
proxy和proxytype,Cloudflare 挑战必须带代理,没有商量余地。 - 解题用一个代理,访问页面又换了一个 —— Cloudflare 拒绝这个
qa_session_cookie。解题和后续访问,代理必须是同一个,一个字符都不能差。 - 用自己浏览器的 User-Agent —— Cloudflare 拒绝请求。只用 API 响应里
user_agent字段返回的那个值,别自己拼一个。 - 请求没带
json=1—— 响应里没有user_agent字段。请求始终带上json=1,响应才会同时包含result(也就是 qa_session_cookie)和user_agent。 - 账号没开代理权限 —— API 直接报错。发请求前先联系 CaptchaAI 支持,把账号的代理权限开通。
- cookie 过期了还在用 —— 过一阵子又开始被拦截。
qa_session_cookie有有效期,一旦请求又开始被拦,就重新解一次。典型有效期是 15–30 分钟。 - 把挑战和 Turnstile 搞混 —— 方法传错,直接解题失败。整页拦截 =
cloudflare_challenge;表单里的小部件 =turnstile,别用混了。
故障排查
ERROR_BAD_PROXY
代理本身连不上,或者被标记为不可用。解决方法:
- 先单独测一下这个代理,能不能连到目标站点。
- 换一个代理试试。
- 检查格式对不对:账号密码认证要写成
login:password@IP:PORT,纯 IP 认证就是IP:PORT。
ERROR_PROXY_CONNECTION_FAILED
CaptchaAI 没能通过你的代理加载验证页。可能是代理临时掉线,也可能是目标站点把这个代理拉黑了。换个代理再试。
ERROR_CAPTCHA_UNSOLVABLE
这次挑战解不了。常见原因:
- 代理太慢或者不稳定。
- 目标站点在 Cloudflare 之外还叠了别的防护。
- 换一个新的代理,重新发一次请求再试。
qa_session_cookie 用过一次就失效了
要么是 cookie 过期,要么是 Cloudflare 把验证机制轮换了。重新解一次,拿到新的 cookie 就行。建议主动监控成功率,在 cookie 过期前提前刷新,不要等到被拦了才补救。
响应里没有 user_agent 字段
请求没带 json=1。不带这个参数,响应就是纯文本,不会包含 User-Agent 信息。Cloudflare 挑战必须始终带上 json=1。
cookie 都设置好了还是 403
按顺序检查这三点:
- 代理和解题时用的是不是同一个。
- User-Agent 是不是原样用了
user_agent响应字段里的值。 - cookie 的 domain 和目标站点是否匹配。
三条都对还是 403,大概率是 cookie 已经过期了,重新解一次。
完整的 CaptchaAI 错误码列表,参考错误处理参考(即将上线)或 API 文档站 docs.captchaai.com。
完整的可运行示例
想要一个包含环境配置、轮询、重试和错误处理的完整项目?
常见问题
国内网络访问海外 Cloudflare 站点,为什么更容易弹出验证页?
Cloudflare 的风控会综合考虑访问来源的 IP 信誉、ASN 归属和历史行为。国内出口 IP、机房 IP、公共云 IP 往往信誉分偏低,触发整页验证的概率明显更高,这和你的代码写得好不好没关系,是网络层面的风控结果。用一个信誉良好、能稳定到达目标站点的代理,是最直接的解决办法。
qa_session_cookie 大概能用多久,要不要定时刷新?
典型有效期是 15–30 分钟,具体因站点而异,有些站点设置的过期时间更长。与其猜测,不如直接监控请求状态:一旦开始收到 403,就说明 cookie 失效了,立刻重新解一次即可。
同一个 qa_session_cookie 能不能给多个并发请求复用?
可以,只要这些请求用的是解题时那同一个代理和同一个 User-Agent。CaptchaAI 按线程计费,一个线程里可以反复用同一个已解好的 cookie 发多次请求,直到它过期为止,不需要每个请求都重新解一次。
一直报 ERROR_CAPTCHA_UNSOLVABLE,是不是我的代理有问题?
大概率是。先单独测试这个代理能不能正常连到目标站点,再看看目标站点是不是在 Cloudflare 之外还叠加了别的防护层。多数情况下换一个更稳定的代理、重新发起请求就能解决。
不带代理能解决 Cloudflare 挑战吗?
不能。这是这个验证码类型的硬性要求,因为 Cloudflare 本身就是靠“解题 IP 和后续访问 IP 必须一致”这条规则来判断合法性的,CaptchaAI 没法绕开这条底层规则。
立即开始解决 Cloudflare 挑战
- 开通代理权限 —— 还没开的话先联系 CaptchaAI 支持
- 拿到 API Key —— captchaai.com/api.php
- 准备好代理 —— HTTP/HTTPS/SOCKS4/SOCKS5 均可,前提是能连到目标站点
- 复制上面 Python、Node.js 或 PHP 的代码 —— 把占位符换成你自己的 Key、URL 和代理
- 后续所有请求都带上返回的
qa_session_cookie+ User-Agent + 同一个代理
如果还是卡住,看看上面的故障排查部分,或者查完整的 Cloudflare 挑战错误码与修复方法。