返回博客

402 Payment Required 的回归:HTTP 状态码如何揭示机器人流量

HTTP 状态码携带的信号比大多数分析工具实际利用的要多。本文讲解服务器端状态码解析如何在不追踪任何人的前提下区分人类与机器人。

大多数 Web 分析决策发生在客户端,也就是页面加载完成、JavaScript 执行之后。这种模型悄悄地假定,每一个抵达你服务器的请求都值得被计入。但事实并非如此。原始流量中有相当大一部分从未渲染任何内容,从未属于真人,也永远不该出现在你的仪表盘里。用于过滤它们的最清晰、也是最早可用的信号,正是你在每次请求中都已经产生的东西:HTTP 状态码。

状态码是一等的分析信号

HTTP 语义规范(RFC 9110)将状态码定义为描述请求结果的三位整数,分为五类:1xx 信息性、2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务器错误。由于 Monoid 运行在 Cloudflare 的边缘网络上,我们能在任何客户端脚本执行之前,就看到源站(或边缘本身)返回的状态码。这个顺序很关键:由浏览器 beacon 记录的页面浏览,只有在文档确实以 2xx 状态被送达并解析之后才可能存在。但请求流中包含的远不止成功的文档。

想想一个典型源站在一天中会返回什么:成功的 HTML 文档、对条件请求返回的 304 Not Modified、因规范化产生的 301/308 重定向、探测扫描器触发的 404,以及故障期间集中出现的 5xx。只统计成功渲染的文档而忽略其余部分,会丢掉能解释流量曲线异常的上下文。

机器人藏在状态分布的哪里

自动化客户端的行为方式与浏览器不同,而状态码能以低成本暴露这种差异。漏洞扫描器在探测管理面板和已知 CMS 路径时,会产生密集的 404 和 403 响应簇。激进的爬虫会无视 429 Too Many Requests(在 RFC 6585 中被定义为限流响应)而持续猛攻,形成任何真人会话都不会产生的特征。粗糙的爬取脚本常常会一路跟随重定向链,而真正的浏览器本会通过 bfcache 或历史记录将其短路。

402 Payment Required 状态码在这里很能说明问题。RFC 9110 明确指出它被保留供未来使用,没有标准化的语义,但近来因计量式 API 与 agent 访问而重新受到关注。如果你的源站开始发出 402,这类流量几乎可以肯定是程序化的,而不是真人在浏览。一个具备状态感知能力的处理管线可以据此打上相应标签,而不是悄悄地虚增某项指标。

为什么纯客户端分析在这里会出错

一个只依赖 JavaScript 的工具几乎无法观察到上述大部分情况。如果某个请求返回 403,没有任何分析脚本会执行,这个事件也就不可见——但它给你的基础设施带来的负载是真实存在的,而其背后的意图(侦察、撞库、爬取)往往正是你真正想了解的东西。相反地,那些不区分状态码、只统计服务器日志行数的工具会高估流量,把每一次 301 跳转和每一次 404 探测都当作一次"命中"。

有用的折中方案是:在边缘层做状态码分类,为一个隐私优先的计数模型提供输入:

  • 带有真实导航的 2xx text/html 是候选的页面浏览。
  • 3xx 应归属于目标页面,不应被重复计数。
  • 4xx 属于诊断信息,而非受众数据——应单独展示。
  • 5xx 属于可靠性面板,应与成功浏览量的任何下降相关联。

在不追踪任何人的前提下做到这一切

这一切都不需要识别访客身份。状态码、方法、响应类别和粗粒度的时间信息,都是请求-响应交换本身的属性,而非某个人的属性。Monoid 从不设置 cookie,从不读取 localStorage,也从不对设备进行指纹识别。我们不需要一个稳定的标识符,就能知道 UTC 时间 03:00 出现的一波 429 是来自自动化程序而非真实受众——状态分布本身就说明了这一点。这正是数据最小化在为我们发挥作用:最不涉及个人信息的信号,恰恰也是衡量流量质量最诚实的信号之一。

这样做还有合规上的好处。在边缘过滤掉非人类流量,意味着从一开始就有更少的虚假事件进入分析存储,让记录的数据更紧密地贴合其声明的用途。根据 GDPR 存储限制原则和目的限制原则(第 5(1)(c) 条和第 5(1)(e) 条),从一开始就不收集垃圾数据,严格来说要优于先收集再事后丢弃。

实践要点

如果你在构建或评估分析工具,请问自己三个问题:它能看到状态码,还是只能看到已渲染的文档?它是否把可靠性信号(5xx)与受众信号(2xx)区分开来?它是否能在不使用标识符的情况下过滤机器人?一个对这三个问题都能回答"是"的处理管线,能同时给你更干净的数据和更小的隐私暴露面——而这一次,二者其实是同一个决定。

参考来源

Comments

Loading comments…