商业化投放接入
把指纹用于广告归因 / 转化回收 / 匿名访客识别有一套行业实践。本页给出 Tripo 内部推荐的接入方式。
强监管领域
浏览器指纹属于个人信息处理,业务侧需在隐私政策中明示告知用途与保留期,符合:
- 中国《个人信息保护法》《数据安全法》《互联网广告管理办法》
- 欧盟 GDPR + ePrivacy(首次访问需 Cookie Banner 同意)
- 加州 CCPA/CPRA
合规是业务侧职责,不是库的职责。
一、不要只靠指纹
工业实践中,指纹只是多信号之一:
一方 ID(登录态)业务侧最强信号,能跨设备打通Click ID(gclid / fbclid / bd_vid / ocid)平台官方归因 ID,从落地页 URL 取,必收服务端 IP / UA / TLS / HTTP/2 指纹网关日志记录,防伪 + 补全浏览器指纹匿名用户去重、跨会话识别 —— 这是本库的位置一方 Cookie / localStorage短期会话续接,配合服务端 Set-Cookie
只有指纹没有 click_id,平台那边对不上账。
二、推荐架构
┌─────────────┐ ┌────────────┐
│ 落地页加载 │ │ 业务后端 │
│ │ │ │
│ 1. 读 click │ POST /track │ - 盐值哈希 │
│ _id │ ───────────────────► │ 入库 │
│ │ { click_id, vid, │ │
│ 2. 计算 vid │ utm, event } │ - 90 天 │
│ (兜底) │ │ TTL │
│ │ │ │
│ 3. sendBea- │ │ │
│ con 上报 │ └────┬───────┘
└─────────────┘ │
│ 转化时
▼
┌─────────────────────────┐
│ 各平台 Conversion API │
│ - Meta CAPI │
│ - Google Enhanced Conv. │
│ - 巨量引擎转化 API │
│ │
│ 传 click_id + 转化事件 │
│ 不传指纹本身 │
└─────────────────────────┘三、前端落地代码
ts
import FP from '@tripo3d/fingerprint';
interface TrackPayload {
vid: string;
click_id: string | null;
utm: Record<string, string>;
event: 'page_view' | 'sign_up' | 'purchase';
ts: number;
}
async function getOrCreateVid(): Promise<string> {
// 优先 localStorage 兜底(Safari ITP 7 天后可能清,需要服务端 Set-Cookie 双写)
let vid = localStorage.getItem('vid');
if (vid) return vid;
const fp = await FP.load();
const { visitorId } = await fp.get();
vid = visitorId;
localStorage.setItem('vid', vid);
document.cookie = `vid=${vid}; max-age=63072000; path=/; SameSite=Lax; Secure`;
return vid;
}
function readClickId(): string | null {
const params = new URLSearchParams(location.search);
return params.get('gclid') ?? params.get('fbclid') ?? params.get('bd_vid') ?? null;
}
function readUtm(): Record<string, string> {
const params = new URLSearchParams(location.search);
const utm: Record<string, string> = {};
for (const k of ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']) {
const v = params.get(k);
if (v) utm[k] = v;
}
return utm;
}
export async function track(event: TrackPayload['event']) {
const vid = await getOrCreateVid();
const payload: TrackPayload = {
vid,
click_id: readClickId(),
utm: readUtm(),
event,
ts: Date.now(),
};
// sendBeacon 即使页面关闭也能发出去
navigator.sendBeacon('/track', JSON.stringify(payload));
}用 requestIdleCallback 触发,别卡首屏
首次 PV 上报建议放到 requestIdleCallback(库内部 load() 已经做了等待)。高频事件(PV)只发 vid,转化事件再带完整 components。
四、服务端要做的事
| 步骤 | 做法 |
|---|---|
| 入库前哈希 | SHA-256(visitor_id + server_secret),原始指纹不落库 |
| TTL | 90 天滚动,过期重算 |
| 归并任务 | T+1 离线把同设备多次访问归并到一个 device_id |
| 转化回传 | 通过各平台 Conversion API(Meta CAPI / Google Enhanced Conv. / 巨量转化 API)回传 click_id + 转化事件 + hashed_email/phone,不传指纹本身 |
五、CSP 兜底
为了防止后续升级版本时被悄悄塞回任何外部请求,建议在站点 CSP 里收紧 connect-src:
Content-Security-Policy:
connect-src 'self' <你的上报域名> <Conversion API 域名>;m1.openfpcdn.io(上游遥测域名)不在白名单里就一定发不出去。
六、稳定性 vs 熵的取舍
广告归因里**「同一个人识别成两个」比「两个人撞成一个」代价大得多**——前者会让平台高估 CPA,烧广告费。所以本库已经:
- 在 Safari 17+ / Firefox 120+ 反指纹模式下走稳定化分支(牺牲熵换稳定)
- 移动端
confidence.score主动降到 0.3-0.5,提示业务侧不要过度依赖
如果你的场景对稳定性更敏感,建议把 userAgent 完整串、fonts 这类高频变化的字段从 hash 里剔除,只保留 Canvas/WebGL/Audio/Screen 等强稳定信号:
ts
import { hashComponents, load } from '@tripo3d/fingerprint';
const fp = await load();
const { components } = await fp.get();
const stable = {
canvas: components.canvas,
webgl: components.webgl,
audio: components.audio,
screenResolution: components.screenResolution,
timezone: components.timezone,
platform: components.platform,
};
const stableId = hashComponents(stable);七、合规清单
业务对接前请确认:
- [ ] 隐私政策写明采集设备特征用于广告归因、保存期限、第三方共享范围
- [ ] 首次访问弹同意框,未同意前只发匿名 PV,不算指纹
- [ ] 提供撤回同意 + 删除数据的入口
- [ ] 与广告平台签 DPA
- [ ] 入库做盐值哈希,不存原始 components
下一步
- Playground — 实测网络面板看是否真的零请求
- API Reference — 完整 API