Comparisons

Cloudflare 浏览器完整性检查与 CAPTCHA 挑战

打开一个 Cloudflare 保护的网站,爬虫返回 403,第一反应常是"验证码没识别对"——但问题往往是浏览器完整性检查(Browser Integrity Check,BIC)在标头层拦下了请求,根本没到 CAPTCHA 这一步。BIC 只看 HTTP 标头,不通过就是 403;CAPTCHA 挑战(Turnstile、JavaScript 挑战、托管挑战)给出真实浏览器能做完的任务,做不完返回 503。分不清两者,很容易把改标头就能解决的问题硬塞给验证码识别服务。

30 秒自查:先看你属于哪一种

  1. 换成浏览器请求头就直接 200 → 只是 BIC 拦截,跟 CaptchaAI 无关。
  2. 标头已经很"浏览器"了,还是 503 → 命中 CAPTCHA 挑战,交给 CaptchaAI。
  3. 换哪种标头组合都还是 403 → IP 或 WAF 问题,换 IP。

拿不准就往下看。


一张表看懂 BIC 和 CAPTCHA 挑战的区别

对比维度 浏览器完整性检查(BIC) CAPTCHA 挑战
检查内容 仅 HTTP 请求标头 浏览器行为 + 运行环境
需要 JavaScript 吗 不需要(标头层拦截) 需要
用户能看到什么 无感通过,或直接 403 挑战小部件或整版 JS 页面
失败时的状态码 403 503
能不能"解决" 不能——只能修标头 能——解出验证码即可
通过后签发的 Cookie qa_session_cookie
需要 CaptchaAI 吗 不需要——改标头即可 需要
默认是否开启 是(所有套餐默认开启) 否(按 WAF 规则配置)
作用范围 每一个请求 只命中匹配的 WAF 规则

BIC 到底在查什么

BIC 在 JavaScript 或 CAPTCHA 挑战之前运行,只判断标头是否像真实浏览器。

BIC 会核对哪几项

  • User-Agent 校验:缺失、为空或已标记异常,如 python-requests/2.31、curl、wget。
  • 标头顺序:不符合真实浏览器习惯,常见于非浏览器 HTTP 库。
  • 缺少常规浏览器标头AcceptAccept-LanguageAccept-Encoding 缺失。
  • 已知机器人特征:扫描器 UA,或非 Google IP 冒充的 Googlebot
  • 恶意 IP 黑名单:命中已知恶意 IP 段或黑名单数据中心 IP。
Request hits Cloudflare edge
    ↓
BIC evaluates HTTP headers:
  ├─ User-Agent present and valid?
  ├─ Required headers (Accept, Accept-Language) present?
  ├─ Header order consistent with browser?
  └─ IP not on abuse list?
    ↓
Pass → Request forwarded to origin
    OR
Fail → 403 Forbidden (no challenge offered)

发送足够"像浏览器"的标头即可,不涉及验证码识别:

import requests

# ❌ FAILS BIC — minimal headers
response = requests.get("https://cloudflare-protected.com")
# User-Agent: python-requests/2.31.0
# Result: 403 Forbidden

# ✅ PASSES BIC — browser-like headers
headers = {
    "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",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,"
              "image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",
}

response = requests.get("https://cloudflare-protected.com", headers=headers)
# Result: 200 OK (passed BIC)

Node.js 版本同理:

const axios = require("axios");

// Browser-like headers to pass BIC
const headers = {
  "User-Agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0",
  Accept:
    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,*/*;q=0.8",
  "Accept-Language": "en-US,en;q=0.9",
  "Accept-Encoding": "gzip, deflate, br",
  Connection: "keep-alive",
  "Upgrade-Insecure-Requests": "1",
  "Sec-Fetch-Dest": "document",
  "Sec-Fetch-Mode": "navigate",
  "Sec-Fetch-Site": "none",
  "Sec-Fetch-User": "?1",
};

async function fetchWithBIC(url) {
  const response = await axios.get(url, { headers });
  console.log(`Status: ${response.status}`);
  return response.data;
}

CAPTCHA 挑战有哪几种

BIC 通过之后才轮到 CAPTCHA 挑战,需要浏览器执行 JavaScript 并给出可解出的任务:

挑战类型 触发条件 用户是否可见 CaptchaAI 对应方法
Turnstile 小部件 开发者手动嵌入页面 表单上的小部件 turnstile
托管挑战(Managed Challenge) 命中 WAF 规则 自适应(可能无感,也可能是勾选框) turnstile
JavaScript 挑战 IUAM 或 WAF 规则触发 "检查你的浏览器"页面 cloudflare_challenge
交互式挑战 风险评分高 整版挑战页面 cloudflare_challenge

多一步 JavaScript 执行:

Request passes BIC check
    ↓
WAF rules evaluate bot score, IP, path
    ↓
Rule triggers CAPTCHA challenge (HTTP 503)
    ↓
Browser executes JavaScript challenge
    ↓
Challenge solved → qa_session_cookie cookie issued
    ↓
Subsequent requests pass with cookie

写一段脚本,自动判断是 BIC 还是 CAPTCHA

与其凭感觉猜,不如分别发送极简标头和浏览器标头,对比状态码和页面内容,直接定位问题类型:

import requests

def diagnose_cloudflare_block(url):
    """Determine if block is BIC or CAPTCHA."""
    # Intentionally use minimal headers to trigger BIC
    minimal_response = requests.get(url, timeout=15, allow_redirects=False)

    # Use browser headers to QA tanılama BIC
    browser_headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                      "AppleWebKit/537.36 Chrome/120.0.0.0",
        "Accept": "text/html,application/xhtml+xml,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
    }
    browser_response = requests.get(
        url, headers=browser_headers, timeout=15, allow_redirects=False
    )

    result = {
        "minimal_headers_status": minimal_response.status_code,
        "browser_headers_status": browser_response.status_code,
    }

    if minimal_response.status_code == 403 and browser_response.status_code == 200:
        result["diagnosis"] = "BIC only — fix headers to pass"
        result["fix"] = "Add browser-like headers"

    elif minimal_response.status_code == 403 and browser_response.status_code == 403:
        result["diagnosis"] = "IP or WAF block — not just BIC"
        result["fix"] = "Change IP or check WAF rules"

    elif browser_response.status_code == 503:
        html = browser_response.text
        if "jschl" in html:
            result["diagnosis"] = "IUAM JavaScript challenge"
            result["fix"] = "Use CaptchaAI cloudflare_challenge method"
        elif "cf-turnstile" in html or "challenge-platform" in html:
            result["diagnosis"] = "Managed/Turnstile challenge"
            result["fix"] = "Use CaptchaAI turnstile method"
        else:
            result["diagnosis"] = "Unknown Cloudflare 验证流程"
            result["fix"] = "Inspect page source for challenge type"

    elif browser_response.status_code == 200:
        html = browser_response.text
        if "cf-turnstile" in html:
            result["diagnosis"] = "Page loads but has Turnstile widget"
            result["fix"] = "Use CaptchaAI turnstile method for form submission"
        else:
            result["diagnosis"] = "No challenge — page accessible"
            result["fix"] = "None needed"

    return result


# Usage
diagnosis = diagnose_cloudflare_block("https://example-cf-site.com")
print(f"Diagnosis: {diagnosis['diagnosis']}")
print(f"Fix: {diagnosis['fix']}")

四种真实场景,先对号入座

举例:杭州一个跨境电商团队抓取海外供应商库存数据,脚本本地跑得好好的,一上云服务器就大面积 403——服务器脚本用 requests 直发请求,标头缺 AcceptAccept-Language,加上数据中心 IP 更敏感,两点叠加被 BIC 拦下。下面四种场景覆盖了同类排查的主要分支。

Request with python-requests UA → 403
Request with Chrome UA → 200 ✓

Fix: Add proper browser headers. No CaptchaAI needed.

场景一,只被 BIC 拦截: 换成浏览器 UA 就能通过,说明只是标头不够"像浏览器",与 CaptchaAI 无关。

Request with Chrome UA → 503 (challenge page)

Fix: Headers are fine. 使用 CaptchaAI solve the challenge.

场景二,BIC 能过、卡在 CAPTCHA: 标头没问题,503 说明命中了 WAF 挑战,这一步才需要 CaptchaAI。

Request with python-requests UA → 403 (BIC block)
Request with Chrome UA → 503 (CAPTCHA challenge)

Fix: First fix headers, then solve CAPTCHA with CaptchaAI.

场景三,BIC 和 CAPTCHA 都要处理: 先改标头消掉 403,还有 503 就再用 CaptchaAI 解决。

Request with python-requests UA → 403
Request with Chrome UA → 403

Fix: Neither BIC nor solvable challenge. IP is blocked.
Try different IP/proxy.

场景四,两个都不是、IP 被封: 换了浏览器标头还是 403,只能换 IP 或代理。


请求头该怎么配置

以下组合可以稳定通过 BIC:

CLOUDFLARE_SAFE_HEADERS = {
    # Mandatory
    "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",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.9",
    "Accept-Encoding": "gzip, deflate, br",

    # Strongly recommended
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",

    # Sec-Fetch headers (modern Chrome)
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1",

    # Recommended for consistency
    "Sec-Ch-Ua": '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
    "Sec-Ch-Ua-Mobile": "?0",
    "Sec-Ch-Ua-Platform": '"Windows"',
}

下面这些写法会直接触发拦截:

  • User-Agent: python-requests/2.31.0 —— 已知 HTTP 库标识。
  • User-Agent: curl/7.81.0 —— 命令行工具标识。
  • 缺少 AcceptAccept-Language —— 真实浏览器不会漏发。
  • User-Agent: ""(空值)—— 明显的自动化特征。
  • 非 Google IP 发出的 Googlebot/2.1 —— 一查 IP 就穿帮。

故障排查速查表

现象 原因 处理方式
极简标头 403,浏览器标头 200 只是 BIC 拦截 换成像浏览器的请求头
换哪种标头都是 403 IP 被封或命中 WAF 规则 换 IP 或走代理
返回 503 并带挑战页面 CAPTCHA 挑战(不是 BIC) 用 CaptchaAI 解决
403 时有时无 限流或会话状态问题 降低请求频率,保持会话一致
标头都对了还是 403 TLS 指纹检测(JA3/JA4) 换用 curl_cffi 或真实浏览器

常见问题

同样一段抓取代码,本地能跑通,一部署到服务器就被拦截,为什么?

本地开发机常用完整浏览器标头;服务器脚本常用最基础的 HTTP 库,标头只剩 UA,数据中心 IP 也更容易被 Cloudflare 重点关照。用诊断脚本分测极简标头和浏览器标头,能快速定位是标头问题还是 IP 问题。

CaptchaAI 能处理 Cloudflare Turnstile 和 Cloudflare Challenge 吗?速度怎么样?

能。CaptchaAI 支持 turnstilecloudflare_challenge,Turnstile 通常 10 秒内解出,Cloudflare Challenge 通常 15 秒内解出,但解决不了 BIC 的 403,那部分只能靠调整标头。

已经通过了 BIC,为什么还会跳出验证码? 两者是独立防线,通过 BIC 只说明标头看起来正常,不代表不会再触发 WAF 规则、机器人评分或 IUAM 判定。

是不是只要看到 503 就一定是验证码? 不一定,也可能是限流或会话异常。先看页面里有没有 cf-turnstilechallenge-platform 特征,再决定该解验证码还是该降频率。

网站可以关闭 BIC 吗?关闭后会不会更容易被刷?

可以,在 Cloudflare 控制台"安全性 > 设置"里关闭,所有套餐默认开启。BIC 几乎不影响正常用户,关闭后风险转移到 WAF 和 CAPTCHA 挑战上,不建议只为兼容爬虫关闭它。


总结

BIC 看的是请求头够不够"像浏览器",通不过给 403,调标头就能解决,不需要 CaptchaAI。CAPTCHA 挑战出现在 BIC 通过之后,返回 503,需要解出 Turnstile 或 Cloudflare Challenge 才能拿到 qa_session_cookie——这一步才轮到 CaptchaAI。先诊断类型:403 修标头,503 用 CaptchaAI,怎么改都还是 403 就换 IP。

相关文章

该文章已禁用评论。