「用哪个浏览器 MCP 绕过 Cloudflare?」是 r/ClaudeAI 上一个至今仍在讨论的问题,而 2026 年大部分时间里的诚实回答是:没有一个能稳定做到,我们的也不例外。九月中旬我们对 v6.6.2 跑过一次机器人检测基准测试,它建议通过住宅代理路由流量。我们配置了。毫无作用。查明原因变成了十一天里的六个版本,这篇文章记录了它们改变了什么,写给每一个需要从 MCP server 抓取受 Cloudflare 保护的网站、又不想伪装成别的东西的人。
CrawlForge MCP v6.7.0 到 v6.12.0 全都围着一个页面:那个以挑战墙的形式返回的页面。普通抓取仍然最先运行,仍然收 2 credits。变化的是它被拒绝之后发生的一切。
目录
- 本次发布了什么,逐版本列出
- 为什么普通抓取在受 Cloudflare 保护的网站上会失败
- 一次调用,三次尝试:升级阶梯
- 解开一次的挑战不再重解
- 智能体自己重试被拦的页面
- 自带代理,现在真的会路由了
- 用基准测试衡量,而非空口宣称
- CrawlForge 不会做什么
- 为客户运行:服务商配置
- 如何升级
本次发布了什么,逐版本列出
| 版本 | 日期 | 一句话概括 |
|---|---|---|
| v6.7.0 | 2026-09-16 | proxyRotation 真正路由流量;Camoufox 不再自称 Chrome |
| v6.8.0 | 2026-09-22 | stealth 基准测试变成 npm run bench:stealth;指纹泄漏已封堵;"auto" 引擎默认使用 Camoufox |
| v6.9.0 | 2026-09-22 | agent 在 stealth 浏览器中重试被拦的页面,8 credits,每次拿到页面的重试加 5 |
| v6.10.0 | 2026-09-25 | 通行凭证罐:cf_clearance、__cf_bm 和 datadome cookies 重放到下一个 stealth 上下文 |
| v6.11.0 | 2026-09-26 | 任何浏览器启动之前先做一次 Chrome TLS 尝试(impit);Chromium 上点击 Turnstile 复选框;升级审计行 |
| v6.12.0 | 2026-09-27 | 应用错误回退页被识别为软拦截;按部署设置 CRAWLFORGE_IMPIT=off |
没有新增或重命名任何工具,除了 agent 的上限之外没有任何价格变化,而这个上限现在取决于有多少页面通过了。上面每一行都取自服务器的changelog。
为什么普通抓取在受 Cloudflare 保护的网站上会失败
Cloudflare 在页面送出之前就给请求打分,而一个 Node HTTP 客户端会同时在三处失分:IP 属于已知的数据中心网段,TLS 握手看起来不像浏览器的,而插页需要客户端永远不会运行的 JavaScript。结果是一个 403,或者一个只带着挑战外壳、别无内容的 200。stealth mode 指南详细讲了各个检测层;这篇文章讲的是服务器现在撞上其中一层时会做什么。
自 v5.6.11 起,scrape 就会把这道墙报告为 success: false,并在 blocked.vendor 里指出 Cloudflare、DataDome、PerimeterX、Akamai、Amazon 或 Vercel,无论 HTTP 状态码是什么,并且不为此收费。自 v5.9.0 起,escalate: true 让同一次调用可以在 stealth 浏览器里把页面渲染一次,而不是返回拦截结果。下面这六个版本,就是这个升级阶段演变成的样子。
一次调用,三次尝试:升级阶梯
在 escalate: true 和默认引擎 "auto" 下,一次 scrape 调用现在会爬一道阶梯,并在第一个返回页面的台阶上停下:
- 普通抓取,带
CrawlForge/<version>User-Agent。2 credits。如果该主机在过去 24 小时内拦过一次请求,MCP server 会跳过这一级,而不是重复一次注定失败的抓取。 - 通过
impit进行的 Chrome TLS 握手,v6.11.0 新增。它呈现 Chrome 的 TLS 指纹,同时仍带着诚实的 CrawlForge User-Agent。不启动任何浏览器。在一个 Cloudflare 会拦下普通抓取的住宅 IP 上,indeed.com 大约一秒钟就回来了。 - stealth 浏览器,Camoufox 优先,Camoufox 二进制缺失时改用 Chromium 并给出警告(v6.8.0)。v6.10.0 的通行凭证罐在这里生效;在 Chromium 上,若正常等待之后墙仍未消失、且页面里有一个
challenges.cloudflare.com框架,就会在 Turnstile 复选框上点击一次(v6.11.0)。
价格是 2 + 5,无论后两级中的哪一级拿到了页面;普通抓取就已完成的调用仍只付 2。在每一级都撞墙的页面会作为拦截结果返回,带着 escalated: true,不收费。
// npm install crawlforge-sdk
import { CrawlForge } from 'crawlforge-sdk';
const client = new CrawlForge({ apiKey: process.env.CRAWLFORGE_API_KEY });
// One call. The plain fetch runs first; only a wall triggers the rest.
const result = await client.scrape({
url: 'https://www.indeed.com/cmp/Burger-King/reviews',
formats: ['markdown', 'metadata'],
escalate: true
});
const data = result.data as {
escalated?: boolean;
stealth?: { engine: string };
content: { markdown: string };
};
// "impit" means the TLS rung got it and no browser ever launched.
console.log(data.escalated, data.stealth?.engine, data.content.markdown.length);impit 这一级里有两道防护。每一次重定向跳转都会用它自己的 DNS 解析做 SSRF 检查,因为 impit 是自己解析域名的。而可见文本不足 200 个字符的页面会继续送往浏览器而不是直接返回:quora.com 对一次 Chrome 握手的回应是其客户端应用的「Something went wrong」回退页,标题却完全正常;v6.12.0 现在在每条路径上都把这种回退页视为软拦截,而不只是这一条。
一个有意为之的差别:托管 REST API 的 scrape 升级仍然只用浏览器。从托管实例的数据中心 IP 测得,impit 这一步没有清除基准测试中的任何一道墙,所以托管服务以 CRAWLFORGE_IMPIT=off 运行,而 npm 安装则保持开启。
解开一次的挑战不再重解
在 v6.10.0 之前,每次 stealth 渲染都是冷启动。如果 Cloudflare 的插页放浏览器过去了,那份通行凭证会随浏览器上下文一起消亡,下一次对同一主机的调用得再解一遍。
通行凭证罐只保留通过了墙的那次渲染中的三个 cookies:cf_clearance、__cf_bm 和 datadome。它们会被重放到下一个引擎、User-Agent 和代理都相同的 stealth 上下文里,也就是凭证当初签发给的那个身份。覆盖范围包括 scrape 的升级阶段、stealth_mode、智能体的 stealth 重试、browser_session 和 scrape_with_actions。
在 stackoverflow.com 上从住宅 IP 测得:第一次 stealth 调用用 4.1 秒穿过了 Cloudflare 的插页,跟踪记录里能看到 orchestrate 和 fo 两个挑战请求。第二次直接得到 200,两者都没有出现,用时 1.6 秒。另一个进程从磁盘复用了一份凭证,加载 indeed.com 时对挑战平台的请求为零。
边界和功能本身一样重要:
- 绝不保留任何其他 cookie。 一个调用方登录产生的会话 cookie 不可能进入另一个调用方的上下文,因为罐子根本不存它。
- 再次撞墙的渲染会丢弃该主机的凭证,所以过期凭证不会形成死循环。
- 过期时间以 cookie 自身为准,上限 24 小时。 罐子最多保存 32 个身份,每个 200 个 cookies。
- 它持久化在
~/.crawlforge/stealth-clearance.json,权限 0600,键经过哈希。CRAWLFORGE_CLEARANCE_JAR=off可将其关闭。
同一阶段曾考虑过一个持久化的 Chromium 配置文件池,并且有意没有做。共享的配置文件会保留登录和站点存储,在托管实例上这会在客户之间泄漏。
智能体自己重试被拦的页面
在 v6.9.0 之前,agent 工具的执行阶段只运行普通抓取。被挑战的种子页面会被丢弃,答案则从搜索摘要里拼出来。在我们自己的评审中,这对 Indeed 上的一个 Burger King 页面产生了「3.3 stars, based on 3.3 reviews」这样的句子 — 评分漏进了摘要的计数字段时就会这样。
现在,遇到挑战、403 或 429、空壳页面或超时的页面,会通过 scrape 所用的同一个升级阶段重试,带着同样的合规闸门、引擎解析器和代理。调用方点名的 URL 最先重试。发现的 URL 只在没有相关搜索摘要时才重试。拒绝、404 和 5xx 错误绝不重试,一次运行最多重试两次,并且剩余时间不足 20 秒时不会开始任何重试。
使用 Chromium 引擎,同一个 Indeed 测试自己读到了种子页面,答出了 3.3 星、来自 58,942 条评论,证据标记为 via: "stealth"。
价格跟随通过的结果:
| 运行 | Credits |
|---|---|
| 不需要重试,或每次重试都撞了墙 | 8 |
| 一次拿到页面的重试 | 13 |
| 两次都拿到页面的重试(上限) | 18 |
结果会报告 stealth_retries(尝试次数)和 stealth_retries_charged(计费次数)。再次撞墙或抛出异常的重试是免费的。REST API 同样收 8 + 每次计费重试 5。
自带代理,现在真的会路由了
CrawlForge 不提供代理。v6.7.0 修好的是:你提供的代理现在真的会被使用。stealthConfig.proxyRotation 曾以三种不同的方式形同虚设:代理被放到 Chromium 的 --proxy-server 标志上,而它承载不了每个住宅代理都会签发的 user:pass,所以需要认证的代理回的是 407;Camoufox 的启动路径在参数列表构建之前就返回了,所以不管怎么要求,Firefox 引擎都在不走代理地运行;而列表只在启动时读取一次,所以 rotationInterval 永远不可能到期。
三处都已修复。代理是普通的 URL(http、https、socks4、socks5,凭据做百分号编码),按浏览器上下文应用,因此两个引擎都能完成认证,格式错误的条目会报错,而不是悄悄发出一个不走代理的请求。Camoufox 现在开启了它自己的 geoip、block_webrtc 和 humanize 特性,所以在代理之后它会从出口 IP 推导语言区域、时区和位置,并与站点看到的地址保持一致。
v6.8.0 添加了服务器级的形式 CRAWLFORGE_STEALTH_PROXIES:一个逗号分隔的列表,供升级阶段、stealth_mode、browser_session、scrape_with_actions 和智能体的重试在调用没有传入代理时使用。调用上的代理永远优先。
同一版本还修复了一个早于这一切的 Camoufox 身份问题。大约三分之二的 Camoufox 上下文曾在 Gecko 引擎上呈现 Chrome User-Agent,并发送 Firefox 从未实现过的 sec-ch-ua 客户端提示。检测器单凭请求头就能据此行动,一行脚本都不用跑。Camoufox 现在呈现它自己的 Firefox 身份,不再注入任何 Chromium 形状的东西。
用基准测试衡量,而非空口宣称
这篇文章里的每一个数字都来自 npm run bench:stealth,v6.8.0 把它从一张手工跑出来的表格变成了一条命令。它驱动 stealth 浏览器和普通抓取去对抗十二道机器人墙和五个检测器页面,并在矩阵开头标注主机操作系统、出口 IP 类别、引擎和浏览器版本,因为从住宅连接得到的 Cloudflare 结果和从数据中心得到的是两个不同的测量。
以它衡量,指纹泄漏已经封堵:navigator.webdriver 读作 false 而不是被删除,navigator 上没有任何自有属性,userAgentData 去掉了它的 HeadlessChrome 品牌,Chrome 主版本号来自已安装的二进制,Web Worker 的回答与文档一致。检测器自检从一份已知的失败清单变成了两个引擎上都是 19 通过、0 失败、1 跳过。npm run bench:stealth:ci 在不访问任何第三方站点的情况下运行其中十项探测,只在出现回归时失败,并带一个把 navigator.webdriver 强制为 true、必须被捕获的负向对照。
基准测试中有两个发现值得记住:
- 哪个引擎能通过取决于出口 IP。 从住宅连接,Camoufox 清除了 indeed.com 而 Chromium 失败了。从托管实例的数据中心地址,两次运行得到的恰好相反。这就是为什么有
CRAWLFORGE_STEALTH_ENGINE来固定一个部署的引擎,为什么托管服务固定用 Chromium,以及为什么 npm 默认仍偏向 Camoufox。 - 单个 DataDome 格子不算结果。 leboncoin 在 Chromium 上一次运行通过了,一小时后从同一个 IP 被拦,中间什么都没变。
尚未封堵的部分在 changelog 里如实写明,而不是一笔带过。Chromium 仍会从 SharedWorker 泄漏 HeadlessChrome,那是 Playwright 不会附加任何东西的目标。而 Turnstile 点击是针对 Cloudflare 的强制交互测试 sitekey 验证的,这只证明了机制本身;真实站点是否接受这次点击,仍取决于它打分的 IP 和指纹。
CrawlForge 不会做什么
搜索者用的词是「绕过」。我们的词更窄,而这几个版本守住了这条线:
- 不解 CAPTCHA,不伪造挑战令牌。 Turnstile 点击只是在 Cloudflare 放到页面上的复选框上点一下,仅限 Chromium。不调用任何令牌 API。
- 每一级的 User-Agent 都是诚实的。
impit握手呈现 Chrome 的 TLS 指纹,同时仍自报为CrawlForge/<version>。在 indeed.com 上,诚实的 User-Agent 4 次运行通过 3 次,而 Chrome User-Agent 通过 0 次,所以诚实的请求头并不是让我们丢掉页面的原因。 - 通行 cookies 绝不在身份之间转移。 通过
impit重放cf_clearancecookie 的方案被否决了,因为该 cookie 绑定于赢得它的那个 User-Agent,重放它就意味着要发送一个浏览器 User-Agent 而不是我们自己的。 - 任何一级运行之前都会检查 robots.txt,使用的是普通抓取所用的同一个
CrawlForge产品令牌。respect_robots: false覆盖会被写入合规审计日志。 - 每次升级都有审计。 自 v6.11.0 起,带
escalate的scrape、智能体的 stealth 重试和stealth_mode各自会在logs/compliance-audit.log写入一行stealth_escalation,时间点在合规闸门之后、浏览器导航之前,记录 URL、工具、解析出的引擎和 API key 的截断哈希。被拒绝的请求不写任何内容。
这份清单,就是一个服务商敢把自己名字放在旁边的抓取器与不敢的那一个之间的差别。
为客户运行:服务商配置
如果你为多个客户运行 CrawlForge,上面这些部件可以组合成一套部署:
# Your residential or ISP proxies. CrawlForge supplies none.
CRAWLFORGE_STEALTH_PROXIES=http://user:pass@proxy-a:8000,socks5://user:pass@proxy-b:1080
# Pin the engine once you have measured which one passes from your exit IP.
CRAWLFORGE_STEALTH_ENGINE=chromium
# Leave the Chrome TLS rung on unless your exit IP is a datacenter range.
# CRAWLFORGE_IMPIT=off
# Clearances persist per engine, User-Agent and proxy; off if you would rather start cold.
# CRAWLFORGE_CLEARANCE_JAR=off然后先测量再承诺:在将要干活的那台机器上运行 npm run bench:stealth,让矩阵标注的是你的出口 IP 类别而不是我们的。审计日志给你一份按 key 划分的每次 stealth 升级记录,可以交给问「代我抓取了什么」的客户;而通行凭证罐的三 cookie 规则意味着一个客户的登录永远不会留在另一个客户的任务复用的上下文里。
对于托管 API,诚实的描述是这样的:stealth 流量从数据中心 IP 出口,升级只用浏览器,而它能清除哪些墙是那个 IP 的属性。当客户的目标站点位于 IP 信誉检查之后时,带你自己代理列表的自托管 MCP server 才是测得好的那套配置。
如何升级
npm install -g crawlforge-mcp-server@latest
crawlforge --version # 6.12.0用 npx 启动服务器的 MCP 客户端会在下次重启时用上 v6.12.0。impit 是一个带预编译二进制的可选依赖;它缺失时,升级的行为与 v6.10.0 完全一致。Camoufox 同样可选,其传递依赖需要 Node 22,"auto" 会回退到 Chromium 并给出说明原因的警告。没有任何工具的 schema 或输出形状发生变化。
想看看这道阶梯从你的 IP 能清除什么?免费开始,赠送 1,000 credits,对一直拦着你的那个页面以 escalate: true 运行 scrape,并阅读 scrape API 参考,了解升级阶段接受的每个参数。