第七部分:浏览统计

本部分是「字数 / 阅读时间 / 浏览量」统计功能的完整教程,包括环境变量配置、Cloudflare Worker 部署与故障排查。

7.1 功能概览

展示位置 内容 数据来源
文章详情页 字数 / 阅读时间 / 浏览量 徽章行 构建期计算 + Umami
页脚 全站总浏览量(与 Built at 同行) Umami 查询
  • 字数与阅读时间:构建时由 lib/reading.ts 计算,写进静态页面,不发请求。
  • 浏览量:页面加载后由 lib/umami.ts 运行时查询 Umami。

三个徽章只显示图标 + 数字,不显示文字标签,含义通过 title / aria-label 走 i18n(六种语言)。

7.2 环境变量

# Umami Website ID(必填,留空则统计功能整体禁用)
NEXT_PUBLIC_UMAMI_WEBSITE_ID=your-website-id

# Umami 分享链接 ID(Share 链接里的那段,推荐用它,只读)
NEXT_PUBLIC_UMAMI_SHARE_ID=your-share-id

# 可选:只读 API Key(优先于 Share ID)
NEXT_PUBLIC_UMAMI_API_KEY=

# 可选:Cloudflare Worker 地址(查询代理 + 上报转发)
NEXT_PUBLIC_UMAMI_PROXY_URL=https://your-worker.workers.dev

禁用方式:

  • NEXT_PUBLIC_UMAMI_WEBSITE_ID 留空 → 不注入统计脚本,徽章与页脚总浏览量整体消失;
  • WEBSITE_ID 有值,但 Share ID / API Key / Worker 地址三者全空 → 同样禁用(没有可用的数据通道)。

注意:所有 NEXT_PUBLIC_ 前缀变量在构建时打进前端 bundle,请只放公开标识符,不要放高权限密钥。修改后需重启 dev server / 重新构建。

7.3 获取 Umami 凭据(免费版)

  1. 注册/登录 cloud.umami.is
  2. 新建 Website(Website URL 填你的站点域名)
  3. 在 Website 设置里复制 Website ID
  4. 创建 Share Link,从分享地址 https://cloud.umami.is/share/{SHARE_ID} 中取出 SHARE_ID
  5. (可选)在设置里创建只读 API Key —— 注意它会明文进前端 bundle,只建议在分享链接不可用时使用

7.4 为什么要部署 Cloudflare Worker

浏览器可能拦截对 cloud.umami.is / gateway.umami.is 的请求(Firefox 增强型跟踪保护、广告拦截插件),导致:

  • 数据上报失败 → 浏览量一直是 0
  • 统计查询失败 → 徽章、页脚总量不显示

scripts/umami/worker.js 在你自己的域名下做两件事:

  1. 查询代理:GET /?path=/posts/xxx/ → 转发到 Umami,返回 {"views": 123}
    • 不带 path 参数即返回全站总量(页脚用)
    • 结果缓存到 CF 边缘 180 秒,Cache-Control: max-age=180
  2. 上报转发:POST /api/send → 转发到官方 gateway,让浏览器只看到向你的 worker 域名发请求

7.5 部署 Worker

  1. 打开 Cloudflare Dashboard → Workers & Pages → Create → Create Worker

  2. 把 scripts/umami/worker.js 全文粘贴进编辑器 → Deploy

  3. 复制 Worker 地址(形如 https://xxx.workers.dev),填入 NEXT_PUBLIC_UMAMI_PROXY_URL

  4. 在 Worker 的 Settings → Variables 里填入必填配置(源码内没有任何真实 ID,缺任一项真实请求一律返回 503 并列出缺失项):

    变量 说明 示例
    WEBSITE_ID Umami Website ID your-website-id
    SHARE_ID Umami 分享链接 ID your-share-id
    ALLOWED_ORIGINS 允许跨域的站点来源,逗号分隔 https://your-domain.example
  5. 重新构建并部署站点

本地开发不需要配置:Origin 为 localhost 时 Worker 直接回示例数据(见下方"示例数据模式")。

安全加固(已内置)

  • CORS 来源白名单:白名单来自 ALLOWED_ORIGINS 环境变量(必填,源码不保存任何真实域名/ID),按请求 Origin 回显,名单外的来源拿不到 Access-Control-Allow-Origin,浏览器会直接拦截;localhost / 127.0.0.1 内置放行(仅示例数据模式)
  • 上报 Origin 校验:带 Origin 且不在白名单 → 403;无 Origin(curl / 服务端脚本)放行,由 payload.website 必须匹配本站兜底,防止 Worker 被当成公共中继给别人刷数据
  • 扩展白名单:需要放行更多域名时,在 Worker 环境变量里加 ALLOWED_ORIGINS=https://a.com,https://b.com

本地开发:示例数据模式

Origin 为 localhost / 127.0.0.1 时,Worker 只回示例、不做真实操作:

请求 返回 行为
查询 {"views":128,"demo":true} 不查 Umami、不进缓存
上报 {"demo":true} 应答后丢弃,不入库

本地开发因此不会污染真实统计,也不消耗 Worker 配额。看到徽章显示 128 是正常现象,部署到线上(正式域名)即为真实数据。

7.6 验证是否生效

  1. 打开你的博客,F12 → Network 过滤 send:应指向 https://xxx.workers.dev/api/send 且状态 200(没有"拦截中")
  2. 浏览几页后,访问 Worker 地址 https://xxx.workers.dev/?path=/,应返回 {"views": n}
  3. 刷新文章页,徽章应显示浏览量 —— 结果有 3 分钟边缘缓存,稍等或强刷即可
  4. 打开 Worker 地址时带上任意 Origin(浏览器访问你的站点即自动带),确认返回的 Access-Control-Allow-Origin 是你的域名

7.7 常见问题

Q: 徽章不显示 / 一直为 0?

A: 按顺序排查:

  1. NEXT_PUBLIC_UMAMI_WEBSITE_ID 和 NEXT_PUBLIC_UMAMI_SHARE_ID 是否已配置(改完要重启 dev server / 重新构建)
  2. 控制台是否有 cloud.umami.is 或 gateway.umami.is 被拦截 —— 被拦就部署 Worker 并配置 NEXT_PUBLIC_UMAMI_PROXY_URL
  3. Umami 后台 Realtime 是否有访客(没有 = 上报被拦,同样走 Worker)
  4. 本地 dev 显示 128 = 示例数据模式,属正常;线上为 0 才是异常
  5. 线上部署后记得在 Cloudflare Pages 环境变量里也配上 NEXT_PUBLIC_UMAMI_PROXY_URL 并重新构建

Q: 浏览量数字有延迟?

A: Worker 结果带 max-age=180 边缘缓存,新浏览量最多滞后 3 分钟。想立即刷新,访问 https://xxx.workers.dev/?path=/posts/xxx/&t=任意随机值(加任意参数换缓存 key,等于强制回源)。

Q: 想关掉浏览统计?

A: 把 NEXT_PUBLIC_UMAMI_WEBSITE_ID 留空即可,统计脚本、徽章、页脚总量会整体消失,字数与阅读时间不受影响。

7.8 总结

步骤 要点
1. 获取 Umami 凭据 Website ID + Share ID
2. 配置环境变量 .env / Cloudflare Pages 两处都要配
3. 部署 Worker(推荐) 粘贴 scripts/umami/worker.js → Deploy
4. 验证 Network 的 send 请求 200 + 后台有访客

完成后,文章页会有「字数 · 阅读时间 · 浏览量」徽章,页脚会显示全站总浏览量。