ad-tracking
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdvertising Analytics & Conversion Tracking Integration
广告分析与转化跟踪集成
Complete reference for integrating Google Analytics 4, Google Ads, Meta (Facebook) Pixel, and LinkedIn Insight Tag into web applications with proper consent management, standard event mapping, user identification, and GDPR/DMA compliance.
将 Google Analytics 4、Google Ads、Meta (Facebook) Pixel 和 LinkedIn Insight Tag 集成到 Web 应用的完整参考,涵盖正确的同意管理、标准事件映射、用户识别以及 GDPR/DMA 合规。
What this skill covers
本 skill 涵盖的内容
GA4, Google Ads, Meta Pixel and LinkedIn Insight Tag, all behind Consent Mode
v2. The body carries the minimal setup and the traps; the tables, schemas and
per-framework wiring live in , listed at the bottom.
references/GA4、Google Ads、Meta Pixel 和 LinkedIn Insight Tag,全部置于 Consent Mode v2 之下。正文提供最小设置和常见陷阱;表格、schema 以及各框架的具体接入方式位于 中,文件列表见底部。
references/Architecture Overview
架构概览
A modern advertising analytics stack consists of multiple pixels/tags that must all respect the same consent state. The architecture follows this pattern:
┌──────────────────────────────────────────────────────────┐
│ CONSENT LAYER │
│ Cookie Consent Banner → localStorage → consent state │
│ All pixels gated by the same consent storage key │
└────────────────────┬─────────────────────────────────────┘
│ granted / denied
┌───────────────┼──────────────────┬──────────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────┐
│ GA4 + │ │ Meta │ │ LinkedIn │ │ Mixpanel │
│ Google │ │ Pixel │ │ Insight │ │ (product │
│ Ads │ │ (fbq) │ │ Tag │ │ analytics)│
└─────────┘ └────────────┘ └──────────────┘ └────────────┘
│ │ │ │
▼ ▼ ▼ ▼
Consent Consent-gated Consent-gated Consent-gated
Mode v2 (render only (render only (opt_in /
(built-in) when granted) when granted) opt_out)Key principle: GA4 uses its own built-in Consent Mode v2 (defaults denied, updates on consent). All other pixels (Meta, LinkedIn, Mixpanel) use a consent-gated pattern — they are not rendered at all until the user grants consent. A shared allows dynamic consent changes without page reload.
CustomEvent现代广告分析栈由多个像素/标签构成,且它们必须遵循同一个同意状态。架构采用以下模式:
┌──────────────────────────────────────────────────────────┐
│ CONSENT LAYER │
│ Cookie Consent Banner → localStorage → consent state │
│ All pixels gated by the same consent storage key │
└────────────────────┬─────────────────────────────────────┘
│ granted / denied
┌───────────────┼──────────────────┬──────────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌────────────┐ ┌──────────────┐ ┌────────────┐
│ GA4 + │ │ Meta │ │ LinkedIn │ │ Mixpanel │
│ Google │ │ Pixel │ │ Insight │ │ (product │
│ Ads │ │ (fbq) │ │ Tag │ │ analytics)│
└─────────┘ └────────────┘ └──────────────┘ └────────────┘
│ │ │ │
▼ ▼ ▼ ▼
Consent Consent-gated Consent-gated Consent-gated
Mode v2 (render only (render only (opt_in /
(built-in) when granted) when granted) opt_out)核心原则: GA4 使用其内置的 Consent Mode v2(默认拒绝,并在同意变化时更新)。其他所有像素(Meta、LinkedIn、Mixpanel)均采用受同意控制的模式——在用户授予同意前,完全不会渲染。通过共享的 ,可在无需重新加载页面的情况下动态处理同意变化。
CustomEventConsent
同意
Read for all seven consent signals, Advanced vs
Basic mode, region-specific defaults, granular consent and GTM wiring. Consent
Mode v2 is mandatory in the EU/EEA since March 2024.
references/consent-mode.mdThe order is the whole thing, and getting it wrong is invisible:
1. dataLayer init + gtag() stub
2. gtag('consent', 'default', { …'denied', wait_for_update: 500 })
3. restore a stored decision → gtag('consent', 'update', …)
4. load gtag.js (async)
5. gtag('js', new Date()) + gtag('config', TAG_ID)Steps 1–3 must run synchronously before gtag.js loads. A page that sets
defaults after the script has loaded looks correct in every test that begins by
accepting consent, and loses the denied population entirely.
Denied is not off. With Advanced mode Google still receives cookieless
pings and models conversions from them (Google's own dated case studies cite
~65–70%; yours is unknown until measured). Whether pre-consent pings are
lawful in your jurisdiction is counsel's call — Basic is the conservative
mode, and it still gets Google's general modeling.
有关全部七项同意信号、Advanced 与 Basic 模式、地区特定默认值、细粒度同意以及 GTM 接入,请阅读 。自 2024 年 3 月起,Consent Mode v2 在 EU/EEA 已是强制要求。
references/consent-mode.md执行顺序决定一切,而且顺序错误往往不易察觉:
1. dataLayer init + gtag() stub
2. gtag('consent', 'default', { …'denied', wait_for_update: 500 })
3. restore a stored decision → gtag('consent', 'update', …)
4. load gtag.js (async)
5. gtag('js', new Date()) + gtag('config', TAG_ID)步骤 1–3 必须同步执行并在 gtag.js 加载之前完成。如果在脚本加载后才设置默认值,那么在每个以接受同意为起点的测试中,页面看起来都正常,却会完全丢失被拒绝的那部分用户。
拒绝并不等于关闭。 在 Advanced 模式下,Google 仍会接收无 Cookie 的 ping,并据此对转化进行建模(Google 自己带日期的案例研究给出的比例约为 65–70%;你的实际比例在测量前未知)。这些同意前 ping 在你所在司法辖区是否合法,需要由法律顾问判断——Basic 是更保守的模式,而且仍会使用 Google 的通用建模。
Google Analytics 4
Google Analytics 4
Setup
设置
Env var: (format: )
NEXT_PUBLIC_GA_MEASUREMENT_IDG-XXXXXXXXXXScript loading:
html
<!-- 1. Consent defaults (MUST be first) -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied', ad_user_data: 'denied',
ad_personalization: 'denied', analytics_storage: 'denied',
wait_for_update: 500
});
// Restore prior consent
try {
if (localStorage.getItem('consent_key') === 'granted') {
gtag('consent', 'update', {
ad_storage: 'granted', ad_user_data: 'granted',
ad_personalization: 'granted', analytics_storage: 'granted'
});
}
} catch(e) {}
</script>
<!-- 2. Load gtag.js -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<!-- 3. Configure -->
<script>
gtag('js', new Date());
// GA4 config carries NO allow_enhanced_conversions — that flag belongs on the
// Google Ads `AW-` tag only (see the Enhanced-conversions section); on a `G-`
// tag it enables nothing and contradicts this skill's own rule.
gtag('config', 'G-XXXXXXXXXX');
</script>环境变量: (格式:)
NEXT_PUBLIC_GA_MEASUREMENT_IDG-XXXXXXXXXX脚本加载:
html
<!-- 1. Consent defaults (MUST be first) -->
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied', ad_user_data: 'denied',
ad_personalization: 'denied', analytics_storage: 'denied',
wait_for_update: 500
});
// Restore prior consent
try {
if (localStorage.getItem('consent_key') === 'granted') {
gtag('consent', 'update', {
ad_storage: 'granted', ad_user_data: 'granted',
ad_personalization: 'granted', analytics_storage: 'granted'
});
}
} catch(e) {}
</script>
<!-- 2. Load gtag.js -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<!-- 3. Configure -->
<script>
gtag('js', new Date());
// GA4 config carries NO allow_enhanced_conversions — that flag belongs on the
// Google Ads `AW-` tag only (see the Enhanced-conversions section); on a `G-`
// tag it enables nothing and contradicts this skill's own rule.
gtag('config', 'G-XXXXXXXXXX');
</script>Commands and events
命令与事件
Read → Commands for / / /
/ with parameter scope and precedence, and
→ Recommended Events for the GA4 names
Google Ads can import (, , , ,
, ) with their required parameters.
references/gtag-api.mdconfigeventsetgetconsentreferences/event-tracking.mdsign_uploginbegin_checkoutpurchasegenerate_leadview_itemSPA page views are not automatic when you send them yourself. Enhanced
measurement fires on History API changes; adding a manual one on
route change double-counts every navigation. Send manual page views only for
hash routing.
page_view有关 / / / / 的命令、参数作用域和优先级,请阅读 → 命令;有关 GA4 可供 Google Ads 导入的事件名称(、、、、、)及其必需参数,请阅读 → 推荐事件。
configeventsetgetconsentreferences/gtag-api.mdsign_uploginbegin_checkoutpurchasegenerate_leadview_itemreferences/event-tracking.md当你自己发送页面浏览事件时,SPA 页面浏览不会自动发生。 Enhanced measurement 会在 History API 发生变化时触发 ;在路由变化时再手动添加一次,会让每次导航都被重复计算。只有使用 hash 路由时才应手动发送页面浏览事件。
page_viewSPA Page Tracking
SPA 页面跟踪
GA4 with enhanced measurement automatically tracks via the History API for Single Page Applications. Manual tracking is only needed for hash routing (). Consent state persists across SPA route changes.
page_viewpage_view#/pathDocs:
- Set up the Google tag
- gtag.js API reference
- Recommended events
- Event parameters
- Data routing & groups
对于单页应用,启用 enhanced measurement 的 GA4 会通过 History API 自动跟踪 。只有在 hash 路由()下才需要手动 跟踪。同意状态会在 SPA 路由变化期间保持不变。
page_view#/pathpage_view文档:
Google Ads Conversion Tracking
Google Ads 转化跟踪
Architecture
架构
GA4 and Google Ads share the same gtag.js snippet. When (format: ) is set, add a second call:
NEXT_PUBLIC_GOOGLE_ADS_IDAW-XXXXXXXXXconfigjavascript
gtag('config', 'G-XXXXXXXXXX'); // GA4
gtag('config', 'AW-XXXXXXXXX'); // Google AdsBoth share consent state, first-party cookies, and the dataLayer pipeline.
GA4 和 Google Ads 共用同一个 gtag.js 代码片段。当 (格式:)已设置时,添加第二次 调用:
NEXT_PUBLIC_GOOGLE_ADS_IDAW-XXXXXXXXXconfigjavascript
gtag('config', 'G-XXXXXXXXXX'); // GA4
gtag('config', 'AW-XXXXXXXXX'); // Google Ads二者共享同一同意状态、第一方 Cookie 和 dataLayer 管道。
Enhanced Conversions
增强型转化
Enhanced Conversions improve attribution by matching hashed user data (email, phone) with Google accounts. Setup:
- Where the flag appears at all, it belongs on the Google Ads config — the
tag, never the GA4
AW-tag (an earlier revision of this skill put it on theG-config, where it enables nothing for Ads):G-
javascript
gtag('config', 'AW-XXXXXXXXX', { allow_enhanced_conversions: true });Checked 2026-08-30: Google's current setup docs
(, ) no longer
document the flag at all — the live mechanism is the account-level toggle
(step 3) plus (step 2). Keep the flag if your tag already
carries it; do not copy it onto the config.
support.google.com/google-ads/answer/9888145…/13258081user_dataG-- Set user data before conversion events fire:
javascript
gtag('set', 'user_data', {
email: 'user@example.com', // Google hashes automatically
phone_number: '+1234567890', // optional
});- Enable in the Google Ads console: Goals → Conversions → Settings →
Enhanced conversions → "Turn on enhanced conversions" (checked 2026-08-31
against ; the old Tools → Conversions path is gone from the redesigned UI)
support.google.com/google-ads/answer/13258081
增强型转化通过将经过哈希处理的用户数据(电子邮件、电话)与 Google 账户匹配来改善归因。设置步骤:
- 只要此标志在任何地方出现,就应放在 Google Ads config——即 标签上,绝不能放在 GA4 的
AW-标签上(本 skill 的早期版本将其放在G-config 上,但在 Ads 中不起任何作用):G-
javascript
gtag('config', 'AW-XXXXXXXXX', { allow_enhanced_conversions: true });核查于 2026-08-30:Google 当前的设置文档(、)已完全不再记录该标志——实际生效的机制是账户级开关(步骤 3)加 (步骤 2)。如果你的标签已经包含该标志,可以保留;不要将其复制到 config 上。
support.google.com/google-ads/answer/9888145…/13258081user_dataG-- 在转化事件触发前设置用户数据:
javascript
gtag('set', 'user_data', {
email: 'user@example.com', // Google hashes automatically
phone_number: '+1234567890', // optional
});- 在 Google Ads 控制台中启用:目标(Goals) → 转化(Conversions) → 设置(Settings) → 增强型转化(Enhanced conversions) → “打开增强型转化(Turn on enhanced conversions)”(依据 核查于 2026-08-31;重新设计后的 UI 已删除旧的工具(Tools) → 转化(Conversions)路径)。
support.google.com/google-ads/answer/13258081
user_id (Cross-Device Tracking)
user_id(跨设备跟踪)
Set GA4 after authentication to enable cross-device tracking and audience building:
user_idjavascript
gtag('config', 'G-XXXXXXXXXX', {
user_id: 'USER_123',
send_page_view: false // prevent duplicate page_view
});身份验证后设置 GA4 ,以启用跨设备跟踪和受众群体构建:
user_idjavascript
gtag('config', 'G-XXXXXXXXXX', {
user_id: 'USER_123',
send_page_view: false // prevent duplicate page_view
});gclid Preservation Through External Redirects
通过外部重定向保留 gclid
The from Google Ads auto-tagging is stored in the first-party
cookie (~90-day default life). It survives external redirects (Stripe Checkout →
return URL) because only expiry ends a cookie on your own domain — no extra code.
Checked 2026-08-31; the caveat is Safari, where ITP caps JS-set cookies at 7
days — 24 hours after a link-decorated navigation, which a gclid landing is.
gclid_gcl_awGoogle Ads 自动标记产生的 会存储在 第一方 Cookie 中(默认有效期约 90 天)。它能在外部重定向(Stripe Checkout → 返回 URL)后保留,因为只有过期才会使你自己域名上的 Cookie 失效——无需额外代码。
gclid_gcl_aw核查于 2026-08-31;需要注意 Safari:ITP 会将由 JS 设置的 Cookie 限制为 7 天——即在带链接参数的导航后 24 小时,而 gclid 落地页正属于这种情况。
Google Ads console setup
Google Ads 控制台设置
Read → Multi-Product Configuration for wiring one
gtag loader to both GA4 and Ads. In the Ads console the conversion action must
be Primary and its attribution window set before the first click lands, or
early conversions are attributed under the old settings and cannot be
recomputed.
references/gtag-api.md有关将一个 gtag loader 同时接入 GA4 和 Ads,请阅读 → 多产品配置(Multi-Product Configuration)。在 Ads 控制台中,转化操作必须设为 Primary,且其归因窗口必须在首次点击产生前设置,否则早期转化会按旧设置归因,且无法重新计算。
references/gtag-api.mdMeta (Facebook) Pixel
Meta (Facebook) Pixel
Setup
设置
Env var: (numeric string)
NEXT_PUBLIC_META_PIXEL_IDBase code (consent-gated):
javascript
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}
(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'PIXEL_ID');
fbq('track', 'PageView');noscript fallback — consent-gated, or omitted. A bare Meta
pixel FIRES on every JS-disabled page view — no banner, no , no consent —
contradicting this stack's rule that Meta is not rendered until consent. So
emit it from the SERVER, only when a server-verifiable consent (a signed
HttpOnly cookie the server reads before writing the HTML) already exists. Cannot
verify consent server-side? Omit it — Meta's verification does not need it,
and a request that fired cannot be taken back:
<noscript>fbqhtml
<!-- server-side: render ONLY when consent is proven -->
{% if consent_cookie_verified and consent.ad_storage == "granted" %}
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=PIXEL_ID&ev=PageView&noscript=1"/></noscript>
{% endif %}Snippets here come from EXECUTABLE templates, so a static check refuses the two
mistakes this section prevents: on a tag, and
a Meta pixel not behind a server-verified consent condition.
allow_enhanced_conversionsG-noscript环境变量: (数字字符串)
NEXT_PUBLIC_META_PIXEL_ID基础代码(受同意控制):
javascript
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}
(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'PIXEL_ID');
fbq('track', 'PageView');noscript 回退方案——受同意控制,否则省略。 裸露的 Meta 像素会在每个禁用 JS 的页面浏览时触发——没有横幅、没有 、没有同意——这与本栈“Meta 在获得同意前不渲染”的规则相冲突。因此应由服务端输出,且仅当已经存在可由服务端验证的同意(即服务端在写出 HTML 前读取的、带签名的 HttpOnly Cookie)时才输出。无法在服务端验证同意?请省略它——Meta 的验证不需要它,而且已经触发的请求无法撤回:
<noscript>fbqhtml
<!-- server-side: render ONLY when consent is proven -->
{% if consent_cookie_verified and consent.ad_storage == "granted" %}
<noscript><img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=PIXEL_ID&ev=PageView&noscript=1"/></noscript>
{% endif %}这里的代码片段来自可执行模板,因此静态检查会拒绝本节防止的两个错误:在 标签上设置 ,以及设置不受服务端已验证同意条件保护的 Meta 像素。
G-allow_enhanced_conversionsnoscriptStandard Events
标准事件
Meta defines 17 standard events. The four that carry a SaaS funnel are
, , and , and only
has mandatory parameters: and , without which Meta
cannot optimise. The full table — every event, its parameters and the traps —
is in → Standard events.
CompleteRegistrationInitiateCheckoutPurchaseSubscribePurchasevaluecurrencyreferences/meta-linkedin.mdMeta 定义了 17 个标准事件。其中,承载 SaaS 漏斗的四个事件是 、、 和 ,且只有 带有强制参数: 和 ,缺少它们时 Meta 无法进行优化。完整表格——包括每个事件、其参数和陷阱——位于 → 标准事件(Standard events)。
CompleteRegistrationInitiateCheckoutPurchaseSubscribePurchasevaluecurrencyreferences/meta-linkedin.mdDeeper Meta and LinkedIn detail
Meta 与 LinkedIn 的更多细节
Read for the parameter object per event, the
firing wrapper, advanced matching (hashed identifiers, and what must never be
sent) and CAPI deduplication.
references/meta-linkedin.md有关每个事件的参数对象、触发 wrapper、高级匹配(经过哈希处理的标识符,以及绝不能发送的内容)和 CAPI 去重,请阅读 。
references/meta-linkedin.mdLinkedIn Insight Tag
LinkedIn Insight Tag
Consent-gated on the same wrapper as the Meta Pixel, env var
. Conversions are configured in Campaign Manager
rather than in code, so there is nothing to deduplicate and nothing that fails
silently in the bundle — but a URL rule on a refreshable page counts every
refresh. Setup snippet, the Campaign Manager steps and are in
→ LinkedIn conversion tracking.
NEXT_PUBLIC_LINKEDIN_PARTNER_IDlintrkreferences/meta-linkedin.md与 Meta Pixel 使用同一个 wrapper 进行同意控制,环境变量为 。转化在 Campaign Manager 中配置,而不是在代码中配置,因此既无需去重,也不会在 bundle 中静默失败——但在可刷新页面上,URL 规则会计入每次刷新。设置代码片段、Campaign Manager 步骤和 位于 → LinkedIn 转化跟踪(LinkedIn conversion tracking)。
NEXT_PUBLIC_LINKEDIN_PARTNER_IDlintrkreferences/meta-linkedin.mdUser Identification
用户识别
One rule the body keeps, because getting it wrong corrupts attribution rather
than losing it: identify before you track, never after. An event fired for an
anonymous id and re-attributed later is two users to every platform that already
ingested it. The per-platform strategy, the Mixpanel vs
distinction and the timing rules are in →
User identification.
aliasidentifyreferences/event-tracking.md正文始终强调一条规则,因为处理错误会破坏归因,而不仅仅是丢失归因:先识别,再跟踪,绝不能反过来。 对匿名 id 触发事件、之后再重新归因,会让每个已经摄取该事件的平台都将其视为两个用户。各平台策略、Mixpanel 的 与 区别以及时序规则位于 → 用户识别(User identification)。
aliasidentifyreferences/event-tracking.mdEvent naming
事件命名
Read → Recommended Events and Parameter
Rules. GA4 reserves a set of names and silently drops events that collide
with them; the reference lists which.
references/event-tracking.md有关 GA4 的推荐事件名称和参数规则,请阅读 → 推荐事件(Recommended Events) 与 参数规则(Parameter Rules)。GA4 保留了一组名称,与这些名称冲突的事件会被静默丢弃;参考文档列出了具体名称。
references/event-tracking.mdE-commerce
电商
Read → Ecommerce Events for the GA4 item
schema and the full purchase / add_to_cart / begin_checkout set.
references/event-tracking.mdDeduplication is the part that silently doubles revenue. A purchase fired
from both the browser and the server (Conversions API, server-side GA4) must
carry the SAME / , or both are counted. Run
rather than reloading the thank-you page:
and fail an
emitter that shares one field and not the other,
fails one that reports from the redirect,
and fails one that drops the pixel. deletes each
rule and watches the matching assertion go red.
event_idtransaction_idfixtures/assert-dedup-contract.mjspixel-and-capi-carry-one-event-idpixel-and-capi-carry-one-event-nameno-purchase-reported-before-the-charge-clearedthe-browser-event-is-kept--self-testA purchase is not a browser event, and that decides where it comes from.
Every other event on the list is something a person did in a tab, so the browser
is its natural source. A purchase is the outcome of a charge that succeeded
inside a payment system, and the only thing that knows it succeeded is the
provider's webhook. Firing it from the thank-you page means firing it from the
one place that has no idea whether the charge cleared — which is why a purchase
event exists for every session that reached the page and a refund exists for
none of them.
The consequence is an ordering, not a preference:
- The webhook handler is the source of truth for the purchase. It writes the entitlement, and the same handler sends the server-side conversion. If the handler is where the money is recorded, it is where the event belongs.
- The browser event stays, and it stays subordinate. Keep it for the
attribution signals only the browser carries (click ids, the consent state,
the session), give it the the server will reuse, and treat the server as authoritative when they disagree.
event_id - Everything the browser loses, it loses closest to the money. Blockers, a mobile browser terminating a tab on redirect, a payment provider's hosted page ending the session before the return leg — each of them removes the last step preferentially, so the browser-only purchase count is biased low by an amount you cannot measure from inside the browser.
Reconcile against the provider rather than the analytics dashboard: the count of
succeeded charges in a period is a number the payment system will give you
exactly, and it is the only external check on this event that is not itself
telemetry. See → Depending on one provider for what else
that reconciliation surfaces.
stripe-billing有关 GA4 商品 schema 以及完整的 purchase / add_to_cart / begin_checkout 事件集,请阅读 → 电商事件(Ecommerce Events)。
references/event-tracking.md去重才是会悄悄让收入翻倍的部分。 如果 purchase 同时由浏览器和服务端(Conversions API、服务端 GA4)触发,必须携带相同的 / ,否则两者都会被计入。请运行 ,而不是重新加载感谢页: 和 会让仅共享其中一个字段而未共享另一个字段的 emitter 失败; 会让从重定向页面报告事件的实现失败; 会让丢弃像素事件的实现失败。 会删除每条规则,并观察对应的断言变红。
event_idtransaction_idfixtures/assert-dedup-contract.mjspixel-and-capi-carry-one-event-idpixel-and-capi-carry-one-event-nameno-purchase-reported-before-the-charge-clearedthe-browser-event-is-kept--self-test购买不是浏览器事件,而这决定了它应从哪里产生。 清单中的其他每个事件都是用户在标签页中完成的操作,因此浏览器是天然的数据来源。购买是支付系统内部成功扣款后的结果,而唯一知道扣款成功的是支付提供商的 webhook。从感谢页触发购买事件,意味着从一个根本不知道扣款是否成功的位置触发它——这正是每个到达该页面的会话都会产生一次 purchase 事件、而退款不会产生对应事件的原因。
由此产生的结论是执行顺序,而不是偏好:
- webhook handler 是 purchase 的事实来源。 它写入权益,并由同一个 handler 发送服务端转化。如果 handler 是记录资金的位置,事件就应该属于那里。
- 浏览器事件保留,但处于从属地位。 保留它是为了归因浏览器独有的信号(点击 ID、同意状态、会话),为它提供服务端将复用的 ,并在二者不一致时以服务端为准。
event_id - 浏览器丢失的一切信息,都会在最接近资金环节的地方丢失。 拦截器、移动浏览器在重定向时终止标签页、支付提供商的托管页面在返回阶段结束会话——每种情况都会优先移除最后一步,因此仅依赖浏览器的 purchase 计数会偏低,而且偏差无法从浏览器内部测量。
应与支付提供商核对,而不是与分析仪表板核对:某周期内成功扣款的次数,是支付系统能够准确给出的数字,也是这个事件唯一不依赖遥测技术的外部校验。有关该核对过程还会发现哪些问题,请参阅 → 依赖单一提供商(Depending on one provider)。
stripe-billingContent Security Policy
内容安全策略(Content Security Policy)
Read → Content Security Policy for the
full directive set per platform.
references/performance-security.mdThe trap: a nonce on the gtag loader is not enough — Google injects further
scripts at runtime, so needs the Google domains as well, and a CSP
that passes on the landing page can still break the checkout where the Ads
conversion tag loads.
script-src有关各平台完整的指令集,请阅读 → 内容安全策略(Content Security Policy)。
references/performance-security.md**陷阱是:**仅给 gtag loader 加 nonce 并不足够——Google 会在运行时注入更多脚本,因此 还需要包含 Google 域名;而且一个能在落地页上通过的 CSP,仍可能在加载 Ads 转化标签的结账页上失效。
script-srcNext.js
Next.js
Read → Next.js / React Integration for the
component tree, and → Script Loading
Strategies for strategy choice and SPA route changes.
references/gtag-api.mdreferences/performance-security.mdnext/scriptTwo traps that are not obvious from either:
- is inlined at build time. In a Docker build the variable must exist in the builder stage; injecting it at runtime leaves the literal string in the bundle and every event goes nowhere.
NEXT_PUBLIC_* - An authenticated area and an embed/widget page need different gating — the dashboard already has consent, an embed may have none and must not assume the parent page's.
有关组件树,请阅读 → Next.js / React 集成(Next.js / React Integration);有关 策略选择和 SPA 路由变化,请阅读 → 脚本加载策略(Script Loading Strategies)。
references/gtag-api.mdnext/scriptreferences/performance-security.md以下两个陷阱从上述内容中都不明显:
- 是在构建时内联的。在 Docker 构建中,该变量必须存在于 builder stage;如果只在运行时注入,bundle 中会留下该字符串的字面值,导致所有事件都发不出去。
NEXT_PUBLIC_* - 身份验证区域和嵌入/widget 页面需要不同的 gating——dashboard 已经获得同意,而 embed 可能没有任何同意状态,不能假设它继承父页面的同意状态。
UTM Attribution
UTM 归因
Read → UTM Attribution for the capture flow
(localStorage through OAuth redirects into the DB), the five-parameter table,
and the platform click IDs (//) — which the SDKs
handle automatically; do not strip them from URLs.
references/event-tracking.mdgclidfbclidli_fat_id有关捕获流程(从 localStorage 经 OAuth 重定向写入 DB)、五参数表和平台点击 ID(//),请阅读 → UTM 归因(UTM Attribution)。这些 SDK 会自动处理点击 ID;不要将其从 URL 中移除。
gclidfbclidli_fat_idreferences/event-tracking.mdVerification
验证
Read → Debug & Testing for the full
per-platform checklist (GA4 DebugView, Meta Test Events, LinkedIn tag helper)
and the consent round-trip.
references/performance-security.mdThe check that catches most of it: open the site in a fresh incognito
window, deny consent, and confirm in DevTools → Network that no or
cookie is set and that collect requests carry . A tag that
fires correctly after consent while ignoring the denied default is the
failure this whole skill exists to prevent, and it looks fine in every test
that starts by accepting.
_ga_gcl_*gcs=G100有关各平台完整检查清单(GA4 DebugView、Meta Test Events、LinkedIn tag helper)和同意状态往返流程,请阅读 → 调试与测试(Debug & Testing)。
references/performance-security.md**最有效的检查:**在全新的无痕窗口中打开站点,拒绝同意,并确认在 DevTools → Network 中没有设置 或 Cookie,且 collect 请求带有 。某个标签在同意后能正确触发,却忽略默认拒绝状态,正是整个 skill 要防止的失败;而且它在每个以接受同意为起点的测试中都看起来正常。
_ga_gcl_*gcs=G100Troubleshooting
故障排除
Read → Troubleshooting for the
common-issues table (consent ordering, duplicate , Advanced/Enhanced
Matching timing, the localhost guard) and the debug-mode snippet. The two seen
most: consent must run before gtag.js loads, and a purchase
without / cannot be optimised against by either platform.
references/performance-security.mdPageViewdefaultvaluecurrency有关常见问题表(同意顺序、重复 、Advanced/Enhanced Matching 时序、localhost guard)和调试模式代码片段,请阅读 → 故障排除(Troubleshooting)。最常见的两个问题:同意 必须在 gtag.js 加载前运行;缺少 / 的 purchase 无法被任一平台用于优化。
PageViewreferences/performance-security.mddefaultvaluecurrencyOfficial documentation
官方文档
Google: Consent Mode v2 ·
gtag.js ·
GA4 events.
Meta: Pixel ·
Conversions API.
LinkedIn: Insight Tag.
Compliance: EU DMA.
Google:Consent Mode v2 · gtag.js · GA4 事件。
Meta:Pixel · Conversions API。
LinkedIn:Insight Tag。
合规:EU DMA。
Deep references
深度参考
The sections above are the integration surface across four platforms; these carry
the depth this file deliberately does not. Each opens with its own Load this
when line, so the trigger has one home and this table stays an index.
| File | Read it when |
|---|---|
| consent is the task |
| you are calling gtag directly |
| you are designing the event schema |
| the tag costs Lighthouse points or fails CSP |
| the work is Meta or LinkedIn rather than Google |
For the page-speed side of the same problem — what the tag does to LCP and TBT,
and what to do about it — see the skill in this pack.
frontend-performance上面的章节是四个平台的集成界面;更深入的内容有意不放在本文中。每个文件都以自己的**何时加载(Load this when)**说明开头,让触发条件各有归属,并使此表仅充当索引。
| 文件 | 阅读时机 |
|---|---|
| 任务是处理同意 |
| 你要直接调用 gtag |
| 你要设计事件 schema |
| 标签影响 Lighthouse 分数或导致 CSP 失败 |
| 工作重点是 Meta 或 LinkedIn,而不是 Google |
有关同一问题的页面速度方面——标签对 LCP 和 TBT 有何影响,以及应该如何处理——请参阅本技能包中的 skill。
frontend-performanceDegradation
降级
- Not Claude Code (Cursor, Codex, the skills CLI, the API container): the pack's
gate does not run. It is the thing that refuses a live key or an irreversible action in this pack; elsewhere those rules are advice you follow by hand.
PreToolUse - Installed by copy rather than as a plugin: the doctrine arrives, does not — same loss, same remedy.
hooks/ - No network, or the ad platform's API unreachable: server-side events cannot be verified end to end. Say the dedup check did not run rather than reporting it held; a browser pixel firing proves the browser, never the conversion.
- 不适用于 Claude Code(Cursor、Codex、skills CLI、API container):本包中的 gate 不会运行。它才是本包中用于阻止实时密钥或不可逆操作的机制;在其他地方,这些规则只是需要手动遵循的建议。
PreToolUse - 通过复制而非作为插件安装:规范已经安装,却不会安装——同样会丢失,也需要采用相同的补救措施。
hooks/ - 无网络或广告平台 API 不可达:无法端到端验证服务端事件。应说明去重检查未运行,而不是声称检查通过;浏览器像素触发只能证明浏览器端正常,绝不能证明转化已完成。
Triggers
触发词
More phrases that route here: "ad tracking", "fbq", "google ads", "gtag", "cookie consent",
"enhanced conversions", "retargeting", "purchase event", "gclid", "UTM", "attribution",
"согласие на куки", "аналитика рекламы".
更多会路由到这里的短语:“广告跟踪(ad tracking)”、“fbq”、“Google Ads(google ads)”、“gtag”、“Cookie 同意(cookie consent)”、“增强型转化(enhanced conversions)”、“再营销(retargeting)”、“购买事件(purchase event)”、“gclid”、“UTM”、“归因(attribution)”、“Cookie 同意(согласие на куки)”、“广告分析(аналитика рекламы)”。