
Cloudflare 验证一直循环,先按这个顺序排:现象分类 → 会话状态 → 浏览器环境 → 请求链路 → IP 与频率风控。不要先批量换 IP,也不要先加脚本补丁。多数循环验证不是单点故障,而是通过验证后的状态没有延续,或者下一次请求在目标站看来已经换了访问身份。
快速判断可以这样做:真人浏览器稳定、自动化浏览器循环,优先查浏览器环境和会话保持;同一环境下换出口后差异明显,再查代理质量、地区、ASN、频率和目标站规则。
先分清:验证循环、403/503,还是接口失败
先把现象拆开,否则后面很容易修错方向。记录三件事:
- 页面是否反复出现 Cloudflare Challenge、Managed Challenge、Turnstile 或 “Just a moment” 中间页。
- HTTP 状态码是 200 但内容仍是 Challenge,还是直接返回 403、503、429。
- 通过一次验证后,是下一次页面访问又回到验证页,还是某个接口、子资源、跳转页失败。
如果状态码是 200,但 HTML 仍是 Challenge 页面,先看 JS 是否执行完成、cookie 是否写入、后续请求是否继续使用同一会话。Cloudflare 官方 Challenge 排障文档也会把浏览器兼容、网络、脚本与配置分开处理,而不是把所有问题都归到 IP。
如果返回 403 或 503,先确认它来自 Cloudflare/WAF 还是目标站应用。保存响应头、页面标题、响应体前 500 字符、cf-ray、server 和重定向链,再进入下一层。
第一层:会话和 cf_clearance 有没有保住
通过 Cloudflare Challenge 后,访问状态通常依赖浏览器会话和 cookie,例如 cf_clearance。Cloudflare 的 Challenge Passage 文档说明,通过状态与有效期、会话持续时间有关。所以“刚通过又循环”不一定是代理问题,也可能是状态没有被保存或复用。
先查这几项:
- 验证通过后,浏览器里是否写入相关 cookie。
- 后续请求是否使用同一个 cookie jar、浏览器 context、用户数据目录。
- 自动化脚本是否在跳转、重试、并发时创建了新的 context 或 session。
- cookie 是否被域名、路径、SameSite、过期时间限制影响。
- 出口 IP、UA、语言、时区、TLS 指纹是否在同一个会话里频繁变化。
不要只复制一次 cookie 到另一个脚本环境里继续请求。目标站看到的不只是 cookie,还包括浏览器环境、请求链路和网络特征。cookie 是状态的一部分,不是完整身份。
一个低成本判断:同一个真人浏览器 profile 连续访问 3 到 5 次。如果不循环,再用持久化 profile 跑自动化浏览器。如果真人稳定、自动化不稳定,优先查浏览器环境;如果真人浏览器也循环,再查网络、扩展、脚本加载或目标站规则变化。
第二层:浏览器环境和 JS 执行是否完整
Cloudflare 验证依赖浏览器侧执行。自动化环境里缺一个关键能力,就可能出现“页面能打开,但一直回到验证页”。
重点检查:
- 是否使用过旧 Chromium、裁剪版浏览器或不完整运行环境。
- 是否禁用了 JavaScript、WebAssembly、Canvas、字体、存储、第三方脚本或必要子资源。
- Headed 与 headless 模式表现是否一致。
- UA、语言、时区、屏幕尺寸、平台字段是否和真实浏览器行为冲突。
- 是否拦截了 Cloudflare 相关脚本、挑战 iframe、Turnstile 资源或验证回调。
- Playwright、Puppeteer、Selenium 的 context 是否每次重建,导致验证状态无法延续。
建议做三组对照:同一台机器、同一出口、同一目标 URL,分别用普通浏览器、headed 自动化浏览器、headless 自动化浏览器访问。普通浏览器正常、headed 正常、headless 循环时,不应先归因到代理。headed 和 headless 都循环时,再比对 cookie、脚本加载、控制台错误和网络请求。
只看截图不够。更有用的是控制台错误、Network 里被拦截或超时的脚本、Challenge 提交后的跳转链、最终落地页 HTML。很多循环验证卡在“验证提交后的状态没有被接受”,不是第一页打不开。
第三层:请求链路有没有拆成多个身份
采集或自动化系统常把一次页面访问拆成多个环节:浏览器打开首页、脚本请求接口、代理层转发、下载 JS 或图片、后端再补请求详情页。只要这些环节身份不一致,目标站就可能重新要求验证。
按顺序核对:
- 首页、跳转页、接口页、静态资源是否走同一个出口策略。
- 浏览器访问和后端 API 请求是否共享同一会话,而不是各自创建身份。
- 重定向前后 Host、Scheme、路径和 cookie domain 是否一致。
- 代理层是否在重试时换了 IP,导致验证通过状态和后续请求不匹配。
- 并发抓取是否把同一个 session 同时用于多个 URL 或多个目标站。
如果链路里既有浏览器请求,又有裸 HTTP 请求,先把流程收敛到单一方式验证。用浏览器完整跑通后,再决定哪些接口可以拆出来。否则常见结果是:浏览器拿到通过状态,后端请求却因为没有同样的会话、请求头和网络特征而重新触发 Challenge。
第四层:再看 IP、代理、频率和目标站风控
前三层基本排除后,再进入 IP 和代理判断。代理会影响 Cloudflare 风控,但不是唯一变量。
用四组对照更容易定位:
- 同一浏览器 profile + 不同出口 IP。
- 同一出口 IP + 不同浏览器环境。
- 同一地区出口 + 不同 ASN 或代理类型。
- 同一 URL + 不同请求频率、并发数和访问路径。
如果同一浏览器环境下,某些出口稳定通过、某些出口持续循环,重点查 IP/代理质量、地区、ASN、历史信誉或目标站区域策略。如果所有出口都循环,问题更可能在浏览器环境、会话保持、脚本执行或目标站规则变化。
频率要单独测。一次访问能过,不代表并发访问也能过。短时间大量触发 Challenge、重复提交验证、并行打开多条会话,都会抬高风险。此时继续增加重试次数通常更差;先降低并发、固定会话、控制访问节奏,再看 Challenge 频率是否下降。
什么时候改用 API/SDK 接入
当问题反复集中在 Cloudflare JS Challenge、Turnstile、cf_clearance、TLS/JA3、返回 HTML/JSON 差异和代理绑定上,继续在浏览器脚本里堆补丁,维护成本会很高。这时可以评估把访问流程收敛到 API/SDK 化方案。
三个条件同时满足时再考虑:
- 场景是授权范围内的公开网页数据采集、自动化测试或业务集成,不涉及未授权访问、账号攻击、恶意注册、撞库、绕过付费墙等。
- 团队已经能说清失败位置:卡在 Challenge 页面、Turnstile token、会话复用、出口 IP、返回内容类型,或某个重定向节点。
- 继续维护浏览器脚本的成本,已经高于统一接入、日志化排查和稳定出口策略。
满足这些条件时,可以查看 Cloudflare 验证与 WAF 场景接入说明。重点不是把问题简单交给工具,而是把会话、代理、浏览器环境和返回格式变成可观测、可复现、可记录的工程接口。需要先了解站点整体能力边界时,可以从 按验证与代理需求查看接入入口 进入。
排查记录:每次只改一个变量
循环验证最怕同时改多个变量。每次只改一项,并记录结果。
建议记录:
- 时间、目标 URL、目标页面类型。
- 浏览器类型、版本、headed/headless、自动化框架版本。
- cookie 是否写入,
cf_clearance是否存在,后续请求是否带上。 - 出口 IP、地区、ASN、代理类型、是否粘性会话。
- 请求频率、并发数、重试次数。
- 状态码、页面标题、响应体特征、
cf-ray、重定向链。 - 修改了哪个变量,Challenge 是减少、消失,还是更频繁。
停止条件也要写清楚:同一环境下多组出口都循环,就先别继续批量换 IP,回到浏览器环境和会话层;真人浏览器也循环,就先别改自动化脚本,检查目标站规则、网络、扩展、脚本加载和本地浏览器状态;只有高并发时循环,就先降频、固定 session、减少重试。
结论:Cloudflare 验证一直循环时,先证明“同一个访问身份能不能稳定延续”,再判断代理和目标站风控。顺序对了,才知道该修会话、修浏览器、修链路、调出口,还是改成更稳定的 API/SDK 接入。事实边界可参考 Cloudflare 官方的 Challenge solve issues 和 Challenge Passage 文档。
