静态站点:轻量访问日志与隐私合规埋点方案

举报
yd_282816122 发表于 2026/11/10 08:57:10 2026/11/10
【摘要】 背景我们的校园导览静态页面,之前完成了图片优化、离线缓存、CSP防护、SRI校验,但缺少基础访问数据:新生、访客更关心哪些景点?轮播曝光、页面停留时长如何?不想引入百度统计、Google Analytics这类第三方SDK,存在CSP拦截、资源白名单、隐私合规风险静态托管,没有独立后端,不能直接写数据库;同时需要规避Cookie追踪、符合最小采集原则很多静态站点埋点方案容易踩坑:过度采集指...

背景

我们的校园导览静态页面,之前完成了图片优化、离线缓存、CSP防护、SRI校验,但缺少基础访问数据:

  • 新生、访客更关心哪些景点?轮播曝光、页面停留时长如何?
  • 不想引入百度统计、Google Analytics这类第三方SDK,存在CSP拦截、资源白名单、隐私合规风险
  • 静态托管,没有独立后端,不能直接写数据库;同时需要规避Cookie追踪、符合最小采集原则

很多静态站点埋点方案容易踩坑:过度采集指纹、未做用户授权、CSP策略被埋点脚本打破、Service Worker缓存把上报请求也缓存了。

核心设计原则

  1. 最小采集:不采集设备指纹、不追踪跨站行为,只采集页面path、访问时间、停留时长、触发事件(轮播切换、景点卡片点击)
  2. 隐私优先:默认不存储持久标识,不使用第三方Cookie;可选本地匿名同意开关
  3. 兼容静态部署:上报接口可使用轻量Serverless函数(云函数/edge function),或者降级输出到日志文件
  4. 和已有体系兼容:适配现有CSP、Workbox缓存策略,埋点请求走fetch并设置no-cache,禁止SW缓存上报请求
  5. 降级兜底:网络异常、CSP拦截时,埋点数据丢而不阻塞页面渲染

前端埋点核心实现

1. 匿名埋点工具(track.js)

// track.js
const TRACK_ENDPOINT = '/api/log';
let startTime = Date.now();
let hasAgree = localStorage.getItem('privacy_agree') === '1';

// 隐私弹窗同意回调
window.setTrackAgree = function(agree) {
  hasAgree = !!agree;
  localStorage.setItem('privacy_agree', agree ? '1' : '0');
};

// 通用上报函数
async function sendTrack(eventName, extra = {}) {
  if (!hasAgree) return;
  try {
    const stayMs = Date.now() - startTime;
    const payload = {
      event: eventName,
      path: location.pathname,
      referrer: document.referrer,
      screen: `${window.screen.width}x${window.screen.height}`,
      stayMs,
      ts: Date.now(),
      ...extra
    };
    await fetch(TRACK_ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
      cache: 'no-cache',
      keepalive: true // 页面关闭依然尽量完成上报
    });
  } catch (e) {
    // 静默失败,不抛出异常影响主业务
    console.debug('track send fail', e);
  }
}

// 页面访问事件
sendTrack('page_view');

// 绑定景点卡片点击
document.querySelectorAll('.spot-card').forEach(card => {
  card.addEventListener('click', () => {
    sendTrack('spot_click', { spotId: card.dataset.spotId });
  });
});

// 轮播切换事件
document.querySelector('.carousel').addEventListener('slideChange', (e) => {
  sendTrack('carousel_switch', { index: e.detail.index });
});

// 页面离开上报最终停留时长
window.addEventListener('beforeunload', () => {
  sendTrack('page_leave');
});

2. CSP 配套修改

原有CSP策略需要放行上报接口,同时禁止埋点脚本外连未知域名:

Content-Security-Policy:
  default-src 'self';
  connect-src 'self' /api/log;
  script-src 'self' 'strict-dynamic';
  img-src 'self' data:;
  style-src 'self' 'unsafe-inline';
  object-src 'none';
  base-uri 'self';

3. Workbox 排除埋点上报请求

在sw配置中增加路由过滤,避免上报POST请求被离线缓存:

registerRoute(
  ({ url, request }) => url.pathname !== '/api/log' && request.method === 'GET',
  new StaleWhileRevalidate()
);

轻量接收层(Serverless示例,Node)

不需要完整数据库,先落地到日志;后续可接入ClickHouse/简单JSONL归档:

// serverless handler
export default async function handler(req) {
  if (req.method !== 'POST') return new Response('405', { status: 405 });
  const body = await req.json();
  // 直接输出日志,托管平台自动收集
  console.log('[track]', JSON.stringify(body));
  return Response.json({ ok: true });
}

如果环境完全不支持Serverless:降级方案,埋点改为输出到localStorage,用户手动导出,适合校内内网静态站点。

隐私合规细节(重点)

  1. 页面增加简洁隐私提示弹窗,未同意前不上报任何数据
  2. 不生成持久唯一用户ID,没有用户画像
  3. 所有日志自动设置保留周期(例如30天自动清理)
  4. 站点底部增加隐私声明页面,说明采集内容、用途、删除方式

工程权衡

✅ 优点

  • 完全自主可控,不依赖第三方统计服务,规避外部域名白名单问题
  • 轻量,脚本体积很小,不会拖累LCP
  • 和我们之前整套静态站流水线打通:构建阶段校验埋点脚本、CSP配置

⚠️ 短板

  • 无后端时只能日志落地,缺少可视化看板,需要额外脚本做日志分析
  • keepalive不保证100%送达,存在少量数据丢失,适合趋势统计,不做精确计数
  • 内网场景下,外网Serverless接口不可用,必须走内网edge函数或者本地存储降级

后续演进方向

  1. 增加日志分析脚本:Node脚本读取日志,生成简单访问报表(热门景点、访问时段)
  2. 增加采样策略:高访问量时按比例采样上报,降低接口压力
  3. 增加异常埋点:捕获前端JS报错,上报错误栈,用于维护校园导览页面
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。