静态站点:轻量访问日志与隐私合规埋点方案
【摘要】 背景我们的校园导览静态页面,之前完成了图片优化、离线缓存、CSP防护、SRI校验,但缺少基础访问数据:新生、访客更关心哪些景点?轮播曝光、页面停留时长如何?不想引入百度统计、Google Analytics这类第三方SDK,存在CSP拦截、资源白名单、隐私合规风险静态托管,没有独立后端,不能直接写数据库;同时需要规避Cookie追踪、符合最小采集原则很多静态站点埋点方案容易踩坑:过度采集指...
背景
我们的校园导览静态页面,之前完成了图片优化、离线缓存、CSP防护、SRI校验,但缺少基础访问数据:
- 新生、访客更关心哪些景点?轮播曝光、页面停留时长如何?
- 不想引入百度统计、Google Analytics这类第三方SDK,存在CSP拦截、资源白名单、隐私合规风险
- 静态托管,没有独立后端,不能直接写数据库;同时需要规避Cookie追踪、符合最小采集原则
很多静态站点埋点方案容易踩坑:过度采集指纹、未做用户授权、CSP策略被埋点脚本打破、Service Worker缓存把上报请求也缓存了。
核心设计原则
- 最小采集:不采集设备指纹、不追踪跨站行为,只采集页面path、访问时间、停留时长、触发事件(轮播切换、景点卡片点击)
- 隐私优先:默认不存储持久标识,不使用第三方Cookie;可选本地匿名同意开关
- 兼容静态部署:上报接口可使用轻量Serverless函数(云函数/edge function),或者降级输出到日志文件
- 和已有体系兼容:适配现有CSP、Workbox缓存策略,埋点请求走
fetch并设置no-cache,禁止SW缓存上报请求 - 降级兜底:网络异常、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,用户手动导出,适合校内内网静态站点。
隐私合规细节(重点)
- 页面增加简洁隐私提示弹窗,未同意前不上报任何数据
- 不生成持久唯一用户ID,没有用户画像
- 所有日志自动设置保留周期(例如30天自动清理)
- 站点底部增加隐私声明页面,说明采集内容、用途、删除方式
工程权衡
✅ 优点
- 完全自主可控,不依赖第三方统计服务,规避外部域名白名单问题
- 轻量,脚本体积很小,不会拖累LCP
- 和我们之前整套静态站流水线打通:构建阶段校验埋点脚本、CSP配置
⚠️ 短板
- 无后端时只能日志落地,缺少可视化看板,需要额外脚本做日志分析
keepalive不保证100%送达,存在少量数据丢失,适合趋势统计,不做精确计数- 内网场景下,外网Serverless接口不可用,必须走内网edge函数或者本地存储降级
后续演进方向
- 增加日志分析脚本:Node脚本读取日志,生成简单访问报表(热门景点、访问时段)
- 增加采样策略:高访问量时按比例采样上报,降低接口压力
- 增加异常埋点:捕获前端JS报错,上报错误栈,用于维护校园导览页面
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)