You need to enable JavaScript to run this app. Next.js自定义document.js按Cookie状态条件加载Google Analytics
优惠活动
大模型
产品
解决方案
定价
更多

Next.js自定义document.js按Cookie状态条件加载Google Analytics

Next.js 实现GA跟踪脚本条件按需注入的方案

之前实现踩坑的核心原因

  • 自定义_document.js的运行机制存在天然限制:它仅在服务端渲染页面外壳阶段执行,静态导出(SSG)构建阶段跑getInitialProps时,根本没有真实HTTP请求对象,所以看到req/res为undefined是必然结果;而且_document不会响应客户端路由跳转的状态变化,本身就不适合做请求级别的Cookie判断和脚本注入逻辑。
  • 硬编码在_document Head里的GA脚本:不管用户最终是否同意跟踪,页面首次加载时就会被浏览器下载,既不符合合规要求,也完全做不到“拒绝就不加载”的条件判断。
  • 关于“读不到浏览器侧Cookie”的误区:首次SSR请求时,浏览器存储的Cookie是会通过HTTP请求头传递到服务端的,只是_document的上下文拿不到完整请求对象,才会出现读不到的问题,换到正确的读取位置就能解决。

分步实现方案

1. 移除原有硬编码的GA脚本

把之前写在_document.js <Head>标签里的所有GA相关script标签全部删除,避免初始加载就加载跟踪脚本。

2. 选择正确的Cookie读取位置

根据项目使用的路由模式,把跟踪Cookie的读取逻辑从_document挪到能拿到完整请求上下文的位置:

  • Pages Router 模式:将Cookie解析逻辑写在_app.js的getInitialProps方法中,这个方法的上下文能拿到完整的req对象(SSR、SSG页面首次加载时都能正常获取请求头里的Cookie)。拿到Cookie后判断:如果跟踪类Cookie值为false,就给_app传递一个isTrackingAllowed = false的props;如果值为true,则标记为允许跟踪。
  • App Router(Next.js 13+)模式:直接在根布局layout.js的Server Component中,通过next/headers导出的cookies()方法读取跟踪Cookie的取值,判断是否允许跟踪即可,完全不需要改动_document。

3. 按跟踪授权状态动态注入GA脚本

根据拿到的授权状态,分两种场景处理注入逻辑:

  • 首次加载已授权的场景
    当读取到Cookie标记为isTrackingAllowed = true时,通过Next.js内置的Script组件加载GA脚本,注意将脚本加载策略设为afterInteractive,避免阻塞页面渲染,示例代码:
    import Script from 'next/script'
    const GA_ID = '你的GA站点ID'
    
    // 仅在用户已授权时渲染Script组件
    {isTrackingAllowed && (
      <>
        <Script
          src={`https://www.googletagmanager.com/gtag/js?id=${GA_ID}`}
          strategy="afterInteractive"
        />
        <Script id="gtag-init" strategy="afterInteractive">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_ID}');
          `}
        </Script>
      </>
    )}
    
  • 用户未选择/拒绝跟踪的场景
    这种场景下初始状态完全不加载任何GA脚本。等用户在Cookie选择横幅上点击“接受跟踪”按钮时,在浏览器端动态完成脚本加载逻辑:
    1. 将用户的选择(值为true)写入对应跟踪Cookie,保证后续访问时服务端能识别授权状态,直接注入GA脚本。
    2. 动态创建script标签加载GA资源,完成gtag初始化。
      示例回调代码:
    const handleAcceptTracking = () => {
      // 写入用户授权的Cookie,有效期1年,路径为全站
      document.cookie = "tracking_consent=true; path=/; max-age=31536000; SameSite=Lax"
      // 避免重复加载
      if (window.gtag) return
      // 动态创建GA脚本标签
      const gaScript = document.createElement('script')
      gaScript.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`
      gaScript.async = true
      document.body.appendChild(gaScript)
      // 初始化gtag逻辑
      window.dataLayer = window.dataLayer || []
      function gtag(){window.dataLayer.push(arguments)}
      gtag('js', new Date())
      gtag('config', GA_ID)
    }
    
    如果用户点击“拒绝跟踪”,就写入tracking_consent=false的Cookie,后续所有页面跳转、服务端渲染流程都会读取到这个标记,永远不触发GA脚本加载。

常见踩坑提醒

  • 不要将GA脚本的加载策略设为beforeInteractive,这类策略会在页面hydrate之前就强制加载,会打破“拒绝跟踪就不加载”的逻辑。
  • 客户端路由跳转时,记得加判断如果window.gtag已经存在,就不要重复注入脚本,避免重复统计。
  • 不需要为了读Cookie强行在_document的getInitialProps里做逻辑,官方本身就不推荐在_document中处理请求级别的业务逻辑,换位置就能解决之前req为undefined的问题。

内容的提问来源于stack exchange,提问作者Re-Angelo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.11 11:27:05