Cloudflare 提供了两种质询操作,站点操作员可以在 WAF 规则中配置:托管质询和 交互式质询。托管挑战是一种现代的自适应方法 — Cloudflare 决定每个访问者的难度。互动挑战是传统选项,始终呈现可见的验证码。了解差异决定了使用哪种 CaptchaAI 方法以及自动化过程中的期望。
快速比较
| 特征 | 管理挑战 | 互动挑战 |
|---|---|---|
| 介绍 | 2021年 | 旧版(2021 年之前) |
| 自适应? | 是(Cloudflare 根据访客决定) | 否(始终互动) |
| 隐形通行证可能吗? | 是(约 90% 的访客以隐形方式通过) | 否(始终显示验证码) |
| 使用的挑战类型 | 隐形→旋转门→ JS挑战 | 验证码始终可见 |
| Cloudflare 推荐? | 是(新规则的默认值) | 否(为了向后兼容而保留) |
| 用户摩擦 | 低(大多数通过但没有看到任何东西) | 高(总是需要交互) |
| HTTP 状态 | 503 | 403 |
| CaptchaAI方法 | turnstile 或 cloudflare_challenge |
turnstile |
管理挑战(现代)
托管挑战是 Cloudflare 推荐的挑战操作。它使用决策框架来提出尽可能最小的破坏性挑战:
决策流程
WAF rule triggers Managed Challenge
↓
Cloudflare evaluates visitor signals:
├─ Browser fingerprint quality
├─ IP reputation score
├─ TLS fingerprint (JA3/JA4)
├─ Request history
├─ Behavioral signals
└─ Device capabilities
↓
Risk assessment → Challenge level selected:
├─ LOW risk → Invisible pass (no visible UI)
├─ MEDIUM risk → Non-interactive Turnstile (background PoW)
├─ HIGH risk → Interactive Turnstile (checkbox/widget)
└─ VERY HIGH risk → JavaScript challenge page (5s wait)
↓
Challenge completed
↓
qa_session_cookie cookie issued
游客体验什么
| 访客类型 | 他们看到了什么 | 百分比 |
|---|---|---|
| 普通浏览器,良好的IP | 什么都没有(隐形通行证) | 〜90% |
| 新浏览器,中立IP | 简短的旋转器 | 〜5% |
| 可疑信号 | Turnstile复选框 | 〜4% |
| 高风险信号 | “正在检查您的浏览器...”页面 | 〜1% |
HTML输出
托管挑战页面使用 Cloudflare 的挑战平台:
<!-- Managed Challenge page (when visible) -->
<body>
<div id="challenge-stage">
<div id="challenge-body-text">
Verifying you are human. This may take a few seconds.
</div>
<!-- Turnstile widget (when rendered) -->
<div class="cf-turnstile"
data-sitekey="0x4AAAAAAAC3DHQhMMQ_Rxrg">
</div>
</div>
<!-- Challenge platform script -->
<script src="/cdn-cgi/challenge-platform/h/g/orchestrate/managed/v1?ray=...">
</script>
</body>
互动挑战(旧版)
互动挑战始终呈现访客必须与之互动的可见验证码。没有隐形的通行证——每个访客都能看到并且必须完成挑战。
它是如何运作的
WAF rule triggers Interactive Challenge
↓
Full-page CAPTCHA served (HTTP 403)
↓
Visitor must interact with CAPTCHA widget
↓
CAPTCHA solved
↓
qa_session_cookie cookie issued
游客体验什么
每个访问者都会看到带有类似旋转门的小部件的整页挑战:
<!-- Interactive Challenge page -->
<body>
<div id="challenge-running">
<div class="main-wrapper">
<h2>Please verify you are human</h2>
<!-- Always-visible challenge widget -->
<div class="challenge-widget">
<!-- Checkbox + verification -->
</div>
</div>
</div>
</body>
为什么 Cloudflare 不鼓励这样做
| 问题 | 影响 |
|---|---|
| 始终可见 | 100% 的访客看到了挑战 |
| 更高的摩擦力 | 每个访客都必须互动 |
| 更高的跳出率 | 用户离开而不是完成 |
| 无风险适应 | 知名访客也受到挑战 |
| 可访问性问题 | 每个访客都必须完成互动 |
检测:我面临哪些挑战?
import requests
import re
def identify_challenge_type(url):
"""Determine if a URL uses Managed or Interactive Challenge."""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
"Accept": "text/html,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
}
response = requests.get(url, headers=headers, timeout=15, allow_redirects=False)
html = response.text
status = response.status_code
result = {
"url": url,
"status": status,
"challenge_type": None,
"cf_ray": response.headers.get("cf-ray", ""),
"solve_method": None,
}
if status == 200:
# Check for inline Turnstile widget (not a challenge page)
if "cf-turnstile" in html:
result["challenge_type"] = "turnstile_widget"
result["solve_method"] = "turnstile"
else:
result["challenge_type"] = "none"
return result
if status == 503:
# 503 indicates Managed Challenge or IUAM
if "managed" in html or "challenge-platform" in html:
result["challenge_type"] = "managed_challenge"
result["solve_method"] = "turnstile" # Managed renders as Turnstile
elif "jschl" in html:
result["challenge_type"] = "iuam_js_challenge"
result["solve_method"] = "cloudflare_challenge"
else:
result["challenge_type"] = "unknown_503"
return result
if status == 403:
if "challenge" in html.lower():
result["challenge_type"] = "interactive_challenge"
result["solve_method"] = "turnstile"
else:
result["challenge_type"] = "waf_block"
result["solve_method"] = None # Hard block, not solvable
return result
return result
# Usage
info = identify_challenge_type("https://protected-site.com/login")
print(f"Challenge: {info['challenge_type']}")
print(f"Solve with: {info['solve_method']}")
检测表
| 信号 | 管理挑战 | 互动挑战 | 国际原子能机构联合会 | WAF区块 |
|---|---|---|---|---|
| HTTP状态 | 503 | 403 | 503 | 403 |
HTML 中的 challenge-platform |
✅ | ✅ | ❌ | ❌ |
managed 脚本中的路径 |
✅ | ❌ | ❌ | ❌ |
HTML 中的 jschl |
❌ | ❌ | ✅ | ❌ |
| 始终显示小部件 | ❌ | ✅ | ❌ | ❌ |
| 隐形通行证成为可能 | ✅ | ❌ | ❌ | ❌ |
解决每种挑战类型
解决管理挑战
托管挑战通常呈现为旋转门小部件。使用CaptchaAI的turnstile方法:
import requests
import time
API_KEY = "YOUR_API_KEY"
def solve_managed_challenge(url, sitekey=None):
"""Solve Cloudflare Managed Challenge."""
# If sitekey not provided, extract from page
if not sitekey:
import re
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0",
}
page = requests.get(url, headers=headers, timeout=15)
match = re.search(
r'data-sitekey=["\']([0-9x][A-Za-z0-9_-]+)["\']', page.text
)
sitekey = match.group(1) if match else None
if not sitekey:
# No visible Turnstile — try cloudflare_challenge method
return solve_js_challenge(url)
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": url,
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Solve timed out")
def solve_js_challenge(url):
"""Fallback to cloudflare_challenge method."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "cloudflare_challenge",
"sitekey": "managed",
"pageurl": url,
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Solve timed out")
解决互动挑战
互动挑战始终显示可见的小部件。使用相同的 turnstile 方法:
def solve_interactive_challenge(url, sitekey):
"""Solve Cloudflare Interactive Challenge (legacy)."""
submit = requests.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "turnstile",
"sitekey": sitekey,
"pageurl": url,
"json": 1,
})
task_id = submit.json()["request"]
for _ in range(60):
time.sleep(5)
result = requests.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY,
"action": "get",
"id": task_id,
"json": 1,
}).json()
if result.get("status") == 1:
return result["request"]
raise TimeoutError("Solve timed out")
Node.js(两种类型)
const axios = require("axios");
const API_KEY = "YOUR_API_KEY";
async function solveCloudflareChallenge(url, type = "managed") {
const method = type === "js_challenge" ? "cloudflare_challenge" : "turnstile";
const sitekey =
type === "js_challenge" ? "managed" : await extractSitekey(url);
const submit = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method,
sitekey: sitekey || "managed",
pageurl: url,
json: 1,
},
});
const taskId = submit.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const result = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: taskId, json: 1 },
});
if (result.data.status === 1) {
return result.data.request;
}
}
throw new Error("Solve timed out");
}
async function extractSitekey(url) {
try {
const response = await axios.get(url, {
headers: { "User-Agent": "Mozilla/5.0 Chrome/120.0.0.0" },
validateStatus: () => true,
});
const match = response.data.match(
/data-sitekey=["']([0-9x][A-Za-z0-9_-]+)["']/
);
return match ? match[1] : null;
} catch {
return null;
}
}
从交互式迁移到托管
Cloudflare 建议从交互式挑战迁移到托管挑战。如果您要自动化切换的站点:
| 改变 | 对自动化的影响 |
|---|---|
| 互动 → 托管 | 可能会开始隐形通过(~90% 的几率) |
| HTTP 403 → 503 | 更新状态代码检查 |
| 始终可见 → 自适应 | 小部件可能不是 HTML 格式 |
| 相同的站点密钥 | CaptchaAI 解决代码保持不变 |
| 相同的 qa_session_cookie 输出 | Cookie 处理保持不变 |
快速检测器工作流程
- 检查页面是否显示间隙质询页面或在应用程序流程中嵌入小部件。
- 在决定处理路径之前,查找 cf-chl 标记、Turnstile 容器和回调挂钩。
- 使用页面 URL 和代理身份记录检测结果,以便快速查看重复的目标更改。
故障排除
| 症状 | 原因 | 处理方式 |
|---|---|---|
| 503 没有可见的小部件 | 管理挑战无形中通过 | 无需采取任何行动 - 您已通过 |
| 503 与“检查您的浏览器” | 成功应对 JS 挑战 | 使用cloudflare_challenge方法 |
| 403 带有可见验证码 | 互动挑战(旧版) | 使用turnstile方法 |
| 403 没有挑战 | WAF 阻塞,不是挑战 | 更改 IP 或请求模式 |
| 挑战类型随机变化 | 适应信号的管理挑战 | 应对 Turnstile 和 JS 挑战 |
| 一种类型的 qa_session_cookie 被其他类型拒绝 | 不同的挑战流程,相同的领域 | 解决出现的任何挑战 |
常见问题
我应该先尝试管理挑战吗?
是的。现在大多数网站都使用托管挑战。从 turnstile 方法开始,如果页面显示 JavaScript 挑战(带有“检查您的浏览器”的 503),则回退到 cloudflare_challenge。
网站可以在不同页面上使用这两种类型吗?
是的,但这种情况并不常见。每个 WAF 规则可以有不同的操作。一项规则可能对 /login 使用托管质询,对 /api/ 使用交互式质询。
托管挑战还需要 qa_session_cookie cookie 吗?
是的。两种挑战类型都会生成相同的 qa_session_cookie cookie。无论质询类型如何,cookie 处理都是相同的。
如果“管理挑战”不知不觉地从我身边经过怎么办?
如果托管挑战确定您的请求风险较低,它会在没有明显挑战的情况下通过该请求。您的响应将是带有页面内容的普通 200。在这种情况下不需要 CaptchaAI 求解。
互动挑战是否已被弃用?
Cloudflare 尚未正式弃用它,但他们建议对所有新规则使用托管挑战。互动挑战仍然是为了向后兼容。站点可能随时迁移。
概括
Cloudflare 的托管挑战会自适应地选择每个访问者的挑战难度(对于完整的 JS 挑战来说是不可见的),而交互式挑战始终会呈现可见的验证码。两者都产生相同的 qa_session_cookie cookie 并通过以下方法解决CaptchaAI— 对于大多数挑战使用 turnstile,对于 JavaScript 挑战页面使用 cloudflare_challenge。管理挑战是现代的默认做法;互动挑战是遗产。