post cover

技术热点落地:Cloudflare 静默注入 Web Analytics——免费计划默认开启的 RUM 与自检关闭清单(2026-08-17)


技术热点落地:Cloudflare 静默注入 Web Analytics——免费计划默认开启的 RUM 与自检关闭清单

热点来源:8/16 Hacker News 用户 stagas 发帖《Tell HN: Cloudflare silently injects its analytics when you switch nameservers》(HN 讨论,525 分 / 146 评论,Algolia API 实测)。他把 nameserver 切到 Cloudflare(为了用 R2 桶在自己子域名上托管静态站 textlog.cc),结果发现这个「HTML-only、无 JS」的站点被静默注入了一段 JS 分析脚本;更离谱的是,Dashboard 里必须先「开启」Web Analytics 才能找到「关闭」入口。Cloudflare 员工 leinwand 在帖内回应确认:免费计划自 2025 年 9 月起默认开启 Web Analytics(RUM),付费计划才是 opt-in;官方博客《The RUM Diaries: enabling Web Analytics by default》(2025-09-17 发布)写明 2025-10-15 起所有免费域名默认开启。一句话剧情:你以为只是「换个 DNS 托管」,实际默认进入了「边缘 HTML 改写 + 真实用户遥测(RUM)」——对追求 HTML 纯净、隐私合规、字节可控的站点,这是一次必须主动处理的隐性变更;本文给出 30 分钟完成的识别 → 关闭 → 验证全流程,以及一份「CDN 信任边界」审计清单。

前情提要


适用场景与目标

它解决什么问题?迁移到 Cloudflare(换 nameserver / R2 自定义域名 / Pages / 套 CDN)后,你的站点 HTML 可能被边缘静默改写:免费计划默认开启 Web Analytics,Cloudflare 会在每个 HTML 响应的 </body> 前插入一段指向 static.cloudflareinsights.com 的 JS(beacon),自动采集真实用户性能数据(RUM),上报到你自己域名下的 /cdn-cgi/rum。对「HTML 纯净(无 JS)、隐私合规、内容字节可控」的站点,这是不可接受的隐性变更。目标:30 分钟内完成识别 → 关闭 → 验证,并把「注入检查」沉淀进迁移 SOP

场景风险建议
换 nameserver 到 CF / 加 R2 自定义域名(OP 场景)🔴 高迁移后立即 curl 检查,默认即注入
免费计划、全站橙云(proxied)🔴 高2025-10-15 起默认开启,按本文第 2 步关闭
付费计划(Pro/Biz/ENT)🟢 低opt-in 才采集,不会自动注入,无需处理
纯 HTML / 无 JS / 静态导出站点🟠 高注入与站点理念直接冲突,关闭 + no-transform 双保险
隐私/合规敏感(医疗、金融、政务)🟠 高关闭是默认动作,别依赖官方「无个人数据」口径
想要免费性能数据(Observatory)🟡 中可主动保留:白拿 RUM + 实验室测试结合的性能面板

不适合的场景(诚实版)

  • 付费计划用户:本来就不会被默认注入,无需折腾。
  • DNS-only(灰云)记录:流量不经过 CF 代理,CF 没有改写能力,天然无注入。
  • 已主动使用 Web Analytics 且依赖 RUM 数据的团队:关掉就没有数据了,请评估后再动手。
  • 只有静态资源/CDN 加速、无 HTML 业务页:注入只发生在 HTML 响应上,资产不受影响。

最小可行方案(MVP)步骤

先跑通(10 分钟):识别 → 关闭 → 验证

  1. 识别:curl 检查 HTML 是否被注入

    curl -s https://yourdomain.com | grep -c cloudflareinsights   # >0 = 已注入
    curl -s https://yourdomain.com | grep -o 'beacon.min.js[^"]*' # 拿到脚本版本路径
    curl -s https://yourdomain.com | grep -o "data-cf-beacon='[^']*'" # 拿到 zone token

    被注入时,</body> 前会出现类似这样的一段(边缘改写,客户端看到的就是这个):

    <script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4513226..."
      integrity="sha512-ZE9pZaUXND66v380QUtch/5sE9tPFh2zg45pR2PB0CVkCtOREv2AJKkSidISWkysEuQ0EH8faUU5du78bx87UQ=="
      data-cf-beacon='{"version":"2024.11.0","token":"c0859b..."}'></script>

    实测:帖子主角 textlog.cc 现在 grep -c cloudflareinsights 返回 0——OP 已关闭,证明「可关、且关得掉」。

  2. 关闭(Dashboard 路径):登录 Cloudflare → 左侧 Analytics & Logs → Web Analytics(域名级左侧菜单也有 Web Analytics 入口)→ Manage Site / Manage RUM Settings → 选 Disable;要彻底移除,点 Advanced Options → Delete。官方博客明确承诺:「一旦你关闭过一次,我们不会再次自动开启它」。

  3. 验证

    curl -s https://yourdomain.com | grep -c cloudflareinsights   # 期望 0

    再开浏览器 DevTools → Network,刷新后过滤 beacon,不应再有 beacon.min.js 请求;/cdn-cgi/rum 不应再收到 POST。

再优化:API 批量关闭(多 zone / 脚本化)

  1. API 路径(适合账号下有多个 zone 的情况,官方博客原文命令):

    # ① 先创建 auto_install=false 的配置(这一步不会注入脚本)
    curl -X POST https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/rum/site_info \
      -H 'Content-Type: application/json' \
      -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_TOKEN" \
      -d '{"auto_install": false, "host": "example.com", "zone_tag": "ZONE_ID"}'
    # 响应里拿到 site_tag(对应下面 $SITE_ID)后二选一:
    # ② 禁用(enabled=false)
    curl -X PUT https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/rum/site_info/$SITE_ID \
      -H 'Content-Type: application/json' \
      -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_TOKEN" \
      -d '{"auto_install": true, "enabled": false, "host": "example.com", "zone_tag": "ZONE_ID"}'
    # ③ 或直接删除配置
    curl -X DELETE https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/rum/site_info/$SITE_ID \
      -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_TOKEN"
  2. 源头规避(不改配置也不被注入):官方文档明确——如果源站返回 Cache-Control: public, no-transformCloudflare 代理将无法修改原始 payload,beacon 不会被注入(Web Analytics 也随之失效)。对纯静态站点(nginx / Caddy / R2),给 HTML 响应加这个头即可一劳永逸;注意它会同时禁用其他边缘改写(见避坑 5)。

关键实现细节

注入机制(root cause):你换 nameserver 时,Cloudflare 默认把记录设为橙云(proxied)——所有 HTTP 流量先经过 CF 边缘。Web Analytics 的 automatic setup 就是在这条链路上做响应改写:识别 HTML 响应 → 在 </body> 前插入 <script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/...">(带 SRI integritydata-cf-beacon 载荷,内含 zone token)→ 浏览器加载后向你自己域名/cdn-cgi/rum 端点上报 RUM 数据(自动安装模式),再由 CF 汇聚到面板。所以「换 nameserver」不是 DNS 变更,而是把响应字节的修改权交给了第三方。

为什么默认开启:2025-09-17 官方博客宣布,2025-10-15 起所有免费域名默认开启 Web Analytics(RUM),默认版本排除 EU/UK 访客流量;官方口径强调「无 cookie、无 localStorage、不按 IP/UA 指纹追踪用户,按『访问事件』而非唯一用户计数」——即 privacy-first 的遥测。官方给免费用户的理由是「性能洞察 + Observatory 免费送」(HN 员工回应原文)。付费计划(Pro/Biz/ENT)是 opt-in,需要手动选择 hostname 与 Automatic Setup 才会注入。

为什么「先开启才能关闭」:官方博客的 opt-out 步骤本身就是「先选 Enable GloballyExclude EU 激活功能 → 再进 Manage RUM Settings → 选 Disable」。10/15 之后免费 zone 的配置处于「已创建但未激活」的中间态,Dashboard 交互上必须激活一次才能进入关闭页——这个 UX 对用户完全不透明,HN 上 csomar 直接说「很难找到关闭这个 JS 的设置」。

判断是否走代理(橙云)dig +short yourdomain.com 返回 CF 网段(如 104.16.x.x / 172.64.x.x)即为 proxied;返回源站 IP 则为 DNS-only。只有 proxied 才存在注入风险。

常见坑与规避清单

#表现规避
1把 CDN 当「DNS 服务商」以为只改了解析,实际把响应改写权交给了第三方橙云 = 边缘可改写内容,按「中间人」审计
2关闭入口难找 / 先开启才能关找不到 Disable 按钮走官方路径:Analytics & Logs → Web Analytics → Manage RUM Settings → Disable;或 API
3免费/付费默认不同免费默认遥测、付费 opt-in,换计划后行为变化迁移当天按免费计划规则处理
4关了不验证 / 边缘缓存残留旧 HTML 仍在 CDN 缓存里带脚本关闭后 Purge Cache 再 curl + DevTools 复查
5no-transform 一刀切所有边缘 HTML 改写全失效只用于「纯静态、不需要 CF 改写」的站点,先看有没有用 Polish/Rocket Loader
6RUM 数据有偏ad-block 用户(Brave/DDG/adblockplus)不贡献数据性能决策别只依赖 RUM,结合实验室测试
7手动 + 自动双装双 beacon 重复上报/渲染冲突只保留一种安装方式(自动或手动 snippet)
8误伤 CSP以为 CSP 能「阻止注入」CSP 只能阻止脚本执行,标签仍在 HTML 源码里,审计以源码 grep 为准

⚠️ 坑 1:Caching 不等于可以修改我的站点(HN 第一怀疑点)

帖子高赞回复 purpleidea 直接甩出被注入的脚本原文并总结:「Caching does not mean modifying my site」。zx8080 更直白:「MITM attack, that’s why this is not in the news」。JoshTriplett 补了一刀:「他们本来就在代理你的 HTML,不修改内容也能统计请求,何必注入 JS」。结论:技术可行 ≠ 默认可做;对内容创作者,「字节是否可控」是原则问题,跟性能数据无关。

⚠️ 坑 2:「免费」的隐性代价是默认遥测

chrisweekly 认为「免费计划 opt-out、付费 opt-in 不算邪恶」,matherial 反驳:「如果『不邪恶』是标准,那我称之为 sleazy(下作)」。员工回应的措辞是「easy to disable」,但 OP 与 csomar 的实际体验都是「找不到开关」。结论:免费计划的默认值就是产品策略的一部分,别指望它贴心,主动处理。

⚠️ 坑 3:关闭后要验证,缓存会骗你

关闭操作改的是配置,但 CF 边缘缓存里可能还躺着旧 HTML(带 beacon 的版本)。只 curl 一次看到 0 也可能是因为关闭前就已经 Purge 过;稳妥顺序:关闭 → Purge Cache → 再 curl → DevTools 确认无 beacon.min.js 请求、/cdn-cgi/rum 无 POST。本文写完后实测 textlog.cc 已为 0,说明这条链路真实可验证。

⚠️ 坑 4:no-transform 是双刃剑

Cache-Control: public, no-transform 是文档明说的「注入免疫」手段,但它的语义是禁止任何中间层修改 payload——Cloudflare 的 Polish(图片优化)、Rocket Loader(JS 延迟加载)、HTML minification、Email Obfuscation 等边缘改写会一起失效,缓存命中策略也可能变化。只在「确定不需要任何边缘改写」的静态站点上用;不确定就先关 Web Analytics 而不是上 no-transform。

⚠️ 坑 5:域名匹配规则与数据偏差

Web Analytics 按 hostname postfix 匹配(配置 example.com 会覆盖 www.example.com、blog.staging.example.com),hostname 不一致会在控制台报 CORS 错——多子域站点关闭时别漏配。另外 FAQ 明说 beacon 会被 adblockplus、Brave、DuckDuckGo 扩展拦截:「无广告用户」恰好是 RUM 数据里缺失的那部分人,用它做性能基线会系统性偏差。

成本 / 性能 / 维护权衡

方案成本性能影响数据/隐私维护
接受默认 RUM(免费)0每 HTML 页多一个第三方脚本请求(SRI + module,可缓存)默认排除 EU/UK、无 cookie;ad-block 用户缺失白拿 Web Analytics + Observatory 面板
Dashboard/API 关闭0无注入无采集每个新 zone 一次;官方承诺关闭后不再自动重开
no-transform 响应头0无注入无采集禁所有边缘 HTML 改写;缓存策略要重新评估
付费计划(Pro+)付费无自动注入opt-in 才采集无需处理

权衡要点:① 对「HTML 纯净」站点这是原则问题,不是性能问题——OP 的 textlog.cc 是 JS-free 站点,注入本身就是内容被篡改;② RUM 数据有真实价值(真实用户感知的 Core Web Vitals、Observatory 免费),但「免费送」的数据也意味着「免费出」的数据,合规自评别省;③ 关闭的边际成本极低(一次 Dashboard 操作 + 一个检查脚本),收益是字节可控;④ 「默认开启」模式意味着每一个新 zone / 新迁移都要查一次——把检查写进迁移 SOP 比记在脑子里可靠。

一周内可执行行动清单

  • Day 1:列出账号下所有 zone(含即将迁移的),逐个跑 curl -s https://<域名> | grep -c cloudflareinsights,输出注入清单
  • Day 2:免费 zone 全部关闭:Dashboard(Manage RUM Settings → Disable)或 API 脚本批量处理(PUT .../rum/site_info/$SITE_ID enabled=false
  • Day 3:Purge Cache 后复查 curl=0,DevTools 确认无 beacon 请求、/cdn-cgi/rum 无上报
  • Day 4:静态托管(nginx/Caddy/R2)加 Cache-Control: public, no-transform(仅当不需要边缘改写),A/B 验证注入确实消失
  • Day 5:把检查脚本接进 CI/CD 或定时任务:curl -s $URL | grep -c cloudflareinsights 断言为 0,非 0 告警
  • Day 6:更新迁移 SOP:换 nameserver / 加 R2 自定义域名 / 上线新 zone 后,必做「注入审计 + 关闭 + 验证」三步
  • Day 7:复盘决策:是否主动启用 RUM(保留数据与 Observatory)?还是彻底关闭走 no-transform?把结论写进团队 wiki

参考资源

写在最后:Cloudflare 默认开启 Web Analytics 不是「事故」而是产品策略——2025-09-17 的博客写得明明白白。真正值得记取的教训是:任何「免费托管 + 代理」的中间层,默认值都是数据与改写权限的让渡。切换 nameserver 的当天花 30 分钟做一次注入审计,你就把「站点字节归谁」这个问题重新握回自己手里。