无 Cookie 的访客统计:我存了什么,没存什么
$ cat /blog/无-cookie-的访客统计-我存了什么-没存什么/ 给博客配了个自建访客统计:一张 D1 表,一段 40 行量级的 Worker 代码,文章页上的“N 次阅读”和后台统计都从它来。这篇不写怎么实现(代码很短),写存了什么、没存什么、为什么——隐私设计才是这类小系统的主菜。
为什么不用现成的
现成方案不缺:CF Web Analytics 功能最全,但数据在别人面板里,和管理员令牌一样属于“平台替你保管”;Plausible、Umami 要么付费要么再养一个服务。对博客这个体量,数据主权和架构简单我都想要,那就自己写——反正也就一张表。
上报:Beacon + 最小字段
前端只在页面加载时发一次 navigator.sendBeacon('/api/track', ...),字段就两个:
- path:当前路径;
- ref:
document.referrer的主机名,只用来回答“访客从哪来”(搜索引擎 / 友链 / 直接访问)。
没有用户 ID,没有 fingerprint,没有 localStorage 标记。同一个访客刷新十次就是十次 pageview——计数是诚实的粗,但我不想为了“去重”去生成一个能跟踪人的标识。
存储的三条纪律
第一,IP 只以天级轮换的哈希存在。sha256(ip + salt + 当天日期),盐放在 Worker 的环境变量里,数据库里查不到原文。第二天盐值日期一变,同一访客的哈希完全不同——它能支撑“今天多少独立访客”这种粗统计,但没法跨天追踪任何人。
第二,referrer 只留主机名。https://www.google.com/search?q=很长的私人文案 进库时只剩 www.google.com。完整的 URL 里经常带着别人的搜索词,那些不该存在我的数据库里。
**第三,没有会话、没有行为链。**不记录“访客 A 先看了哪篇又看了哪篇”。我需要回答的问题只有三个:多少人来了、从哪来、看了哪些文章。答不了的问题干脆不收集——不收集是最彻底的隐私保护。
界面上有个小设计
自己的访问不该污染读者数据。统计页是令牌门禁的独立页面,管理访问不带 referer 上报的路径——访客统计里的数字,就是访客的数字。
复盘
这套统计的总成本:一张 D1 表、一个端点、几十行代码。换来的是:页面干净(无脚本横幅)、用户清爽(无 cookie 无追踪)、我自己清楚每一个字节是什么。
安全这件事,在小系统上的最优解往往不是“加”,是**“少存”**。