← 全部文章

浏览器 Profile:自定义 request 指纹外观

从包含 79 个实测 profile 的公开目录中,为每个 request 指定呈现的浏览器、OS 及版本。当目标站点拒绝某个 profile 时,轮换机制会自动切换。

更新内容

轮换出口 IP 是大家都会自动化的部分。但其底层的浏览器特征通常从未改变。

Single 和 Proxy Finder 现在支持四个可选字段,用于决定请求所呈现的浏览器特征:browser、os、version 和 profile。背后的目录已在 GET /api/profiles 公开,无需 API key。目前它包含 79 个 profile,涵盖 Windows、macOS、Android 和 iOS 上的 Chrome、Edge、Safari、Firefox 和 Tor。

当你不指定 profile 时,Proxy Finder 会为你自动更换。但仅在目标站点两次明确拒绝你当前发送的特征之后才会执行。

工作原理

其中三个字段面向人工配置,一个面向机器处理。

browser、os 和 version 用于筛选目录,你可以发送它们的任意子集。os 按系列匹配,因此请求 macOS 会接受列表中所有的 macOS 版本,而请求确切的版本标签则会精确匹配该版本。当仍有多个 profile 符合条件时,最新版本优先,因为保持最新才是核心目的。旧的主要版本正是封禁列表重点识别的对象。

curl -X POST https://eu.api.foura.ai/api/single/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "GET",
    "url": "https://example.com/listing/42",
    "browser": "Safari",
    "os": "iOS"
  }'

这会解析为目录中最新的 iOS 版 Safari,并按该顺序发送对应的 User-Agent、client hints 以及 header 顺序。Header 顺序本身就是一个特征信号,因此发送时不会进行任何排序处理。

profile 是第四个字段:来自目录的精确 id,适用于在新版本发布后仍必须保持发送相同 client 的代码。Proxy Finder 在其 request 对象中接收所有这四个字段。完整的参数列表请参见 API 参考,Playground 也会读取相同的目录,因此其下拉菜单只提供你代码可以请求的选项。同样的四个字段也包含在 MCP server 上的 foura_single 和 foura_proxy 中,这样 agent 就可以作为另一个浏览器重试,而不是直接返回一个单纯的 403。

此设计特意拒绝执行两件事。

如果目录无法提供某种组合,则会报错并列出该浏览器当前可用的选项。回退到默认设置会发送一个你并未选择且无法在 response 中看到的 client。带有 unblocker: false 的 profile 选择也会被拒绝,因为该 flag 负责携带 header(该 flag 的实际作用)。残缺的 profile 比没有更糟糕。

目录本身是实测得出的,而不是手动编写的。脚本会通过真实的 request 路径触发每个 profile,并记录网络传输中的实际内容。这比听起来更重要:在某款浏览器最近的两个大版本之间,占位符品牌字符串发生了变化,品牌顺序也颠倒了,而这正是检测服务所关注的细节。

影响

这是我们没有料到的部分。

默认设置是共享的默认设置。当你什么都不指定时,你的 request 所呈现的 client,与所有未做指定的 request 所呈现的完全相同;而想要以低成本进行特征识别的防御机制,恰恰就针对这一点。它的失败方式也很特殊:一个拒绝某种浏览器的网站,会在你拥有的每一个出口节点都拒绝它。你会耗尽所有的重试配额,只为了证明同一个 client 不受支持。

我们在三家供应商上进行了三次测试,每次的情况都如出一辙。

一个受 PerimeterX 保护的房产门户网站在默认设置下连续拒绝了 9 次尝试。仅更改 request 声明的 platform(其他一切保持不变,使用相同的 pool),6 次尝试全部成功返回页面。一家受 Akamai 保护的保健品零售商拒绝了默认设置,却毫无阻碍地响应了另外两个浏览器家族。一个受 DataDome 保护的财经新闻网站对默认设置返回了 401 和一个 774 字节的插页,而另外三个 profile 则成功拉取了约 760 KB 的真实页面,各成功执行了 12 次。我们正向和反向都测试了一遍,以确保不是请求顺序在起作用。

因此 Proxy Finder 现在不仅轮换出口节点,还会轮换浏览器系列。必须有两个独立的出口节点拒绝后它才会切换,因为单次拒绝只是单个节点的判定。随后它会沿着梯度逐步调整:从最小的变更(平台)开始,之后才会尝试其他系列。

这不需要额外成本。轮换只改变重试时发送的内容,绝不改变是否触发重试,因此每个任务消耗的 request 和 credit 额度与之前完全一致。

进阶用户指南

轮换机制不会干扰自定义配置,了解其运作规则很有必要。

如果你自行指定了 profile,它绝不会触发。当你发送了自己的 user-agent 或 cookie header 时也绝不触发,这一点至关重要:通关 cookie 与获取它的客户端绑定,因此在有效 session 重放期间轮换底层签名,会导致原本正常的 request 失败。固定你需要的配置,它就会保持固定。

也并非所有失败都会作为切换 profile 的依据。被识别的安全防护厂商判定算数。没有明确厂商名称的纯拒绝状态码 (401, 403, 429, 503) 也算数,事实证明这一点非常关键。曾经有一个任务返回了 8 次状态码拒绝,且没有命中任何已知防护特征:这些是真实的拒绝,但轮换机制之前忽略了它们,因为当时它只信任检测器。页面不存在或国家区域封锁则不算数。这些是对你的 URL 和地理位置的响应,与客户端无关。

所有这些状态都是透明可见的。成功的 Proxy Finder response 仅在我们自动选择时才携带 profile,由你指定时则不会携带。失败的任务会携带 attemptReport.profilesTried:记录按首次使用顺序尝试过的系列,若 request 未作任何修改直接发出则标记为 default。如果没有这个字段,从外部来看,“我们尝试了四种浏览器且全部被拒”与“我们从未更改过浏览器”没有任何区别。

一个值得借鉴的习惯:在测试某个 profile 是否有效时,保持所有其他变量不变。Scrapfly 在其 2026 年指纹测试工具综述中说得很明确:在两次运行之间更改三个变量,只能说明有些改动起效了,却无法确定是哪一项起了作用。相同的目标,相同的出口节点,仅变更一个字段。上文中的所有数据也都是通过这种方式得出的。

未来规划

候选梯度的扩充完全依赖实测,逐个目标推进。只有当我们实际观察到某个系列成功访问了默认配置无法打开的目标时,才会将其加入,而不是凭空猜测;并且每次引擎升级时都会重新测量目录,而不是直接沿用旧数据。

这就是该领域棘手的地方。最优的客户端签名是一个不断变化的目标,对其追踪是一项持续的工作,而非一次性交付的任务,上个季度有效的方案现在很可能已被列入封禁名单。这也是为什么我们将其设计为一个可配置的字段和一个可查阅的目录,而不是由我们替你硬编码的固定数值。