Google Publisher Tag (GPT) 不加载

从 AdSense `<ins>` 换到 GPT 想要更多控制——slot 不填、console 报 GPT 错。首次用 GPT 的常见原因。

你从 AdSense Auto Ads(或 <ins class="adsbygoogle">)换到 Google Publisher Tag(GPT),因为 Ad Manager 给你广告调度、header bidding、line item 定向这些精细控制。gpt.js 接好了,defineSlotdisplay 都调了,部署上线——所有 slot 仍然是空的。DevTools console 要么报 googletag is not defined,要么报 defineSlot was not called for slot,要么什么也没有。GPT 失败基本归到五类:脚本根本没加载、slot DOM id 和 defineSlot 不一致、googletag.cmd.pushgpt.js 之前跑、SPA 路由切换导致双初始化、广告拦截扩展。这篇按命中率排序走完每一类,给出具体的 console 诊断和最小可用代码。

10 秒判断你是哪种情况

DevTools console 依次敲:

看到的提示最可能的原因
Uncaught ReferenceError: googletag is not definedgpt.js 根本没加载(网络或广告拦截)
googletag.pubads().getSlots() 返回 []defineSlot 没执行
getSlots() 有 slot 但 <div> 还是空defineSlot 的 id 和 DOM <div> 的 id 不一致
警告 defineSlot was not called for slot ...display() 在 defineSlot 之前调,或 slot id 拼错
两个 googletag iframe 加载、一个空白双初始化(脚本写了两份,或 SPA 路由切换重跑了 setup)

常见原因(按命中率排序)

1. gpt.js 被广告拦截器 / CSP / 网络挡了

uBlock Origin、Brave Shields、AdGuard、企业网络默认都拦 securepubads.g.doubleclick.net。如果你没在 Content Security Policy 里白名单 Google 广告域名,CSP 也会挡。

怎么判断:DevTools → Network → 筛 gpt。如果 gpt.js 标红、状态 (blocked:csp)(blocked:other),脚本压根没下载,后续访问 googletag 必然报 is not defined

修复

  • CSP:script-src 白名单 https://securepubads.g.doubleclick.net,frame-src 和 img-src 白名单 https://*.googlesyndication.com
  • 用全新的 Chrome profile(关掉所有扩展)测一遍,确认是客户端拦截不是代码问题。
  • 别想着「绕开」广告拦截器——优雅降级:GPT 没加载就渲染你自己的内容或干净的空白。

2. 没用 googletag.cmd.push 模式

正确写法是每个 GPT 调用都放进 googletag.cmd.push(function() { ... })cmd 是个命令队列,gpt.js 加载完后会清空。如果你在脚本加载前直接调 googletag.pubads(),会抛异常。

<!-- 错:race condition -->
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
  googletag.defineSlot('/123/leaderboard', [728, 90], 'banner').addService(googletag.pubads());
  googletag.enableServices();
</script>

<!-- 对:用 cmd.push 队列 -->
<script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
<script>
  window.googletag = window.googletag || { cmd: [] };
  googletag.cmd.push(function() {
    googletag.defineSlot('/123/leaderboard', [728, 90], 'banner').addService(googletag.pubads());
    googletag.pubads().enableSingleRequest();
    googletag.enableServices();
  });
</script>

原理googletag.cmd = [] 是内联同步初始化的,cmd.push 可以立即调;gpt.js 一加载完就把 cmd 替换成一个会立刻执行 callback 的函数。这是标准异步 loader 模式。

3. slot div 的 id 和 defineSlot 不一致

defineSlot(adUnitPath, sizes, divId)googletag.display(divId) 必须引用和 <div> 完全一样的 id——大小写也算。任何不一致 slot 就是空的。

<!-- HTML -->
<div id="div-gpt-ad-leaderboard"></div>

<!-- 脚本 -->
<script>
googletag.cmd.push(function() {
  googletag.defineSlot('/123/leaderboard', [728, 90], 'div-gpt-ad-leaderboard')
    .addService(googletag.pubads());
  googletag.enableServices();
  googletag.display('div-gpt-ad-leaderboard');
});
</script>

怎么判断:console 敲 googletag.pubads().getSlots()[0].getSlotElementId() 看 GPT 认为要塞哪个 id,再对照 DOM 里 <div id="...">。有差异 slot 就填不进去。

4. SPA 路由切换导致双初始化

React / Vue / Astro 客户端路由切换时组件会 remount。如果 defineSlot 每次 mount 都跑,重复定义会被 GPT 静默拒绝。更糟的是 enableServices() 整个页面生命周期只应该调一次。

怎么判断:每次切路由 googletag.pubads().getSlots().length 都在涨;只有第一个 slot 填了,后面的都空。

修复:slot 在 app 入口定义一次,路由切换时用 googletag.pubads().refresh([slot]) 重新拉广告而不是重新定义:

// App 入口——只跑一次
window.googletag = window.googletag || { cmd: [] };
googletag.cmd.push(function() {
  window._adSlot = googletag.defineSlot('/123/leaderboard', [728, 90], 'div-gpt-ad-leaderboard')
    .addService(googletag.pubads());
  googletag.pubads().enableSingleRequest();
  googletag.enableServices();
});

// 路由切换 handler
function onRouteChange() {
  googletag.cmd.push(function() {
    googletag.pubads().refresh([window._adSlot]);
  });
}

5. 你其实想用的是 AdSense 不是 GPT

GPT 属于 Google Ad Manager(原 DFP)。只有 AdSense 账号是用不了 GPT 的——你的广告位路径 /12345/slot-name 在 Ad Manager 找不到任何 inventory。Ad Manager 的 network code 自助开户 21 位起,老账号短一些;AdSense 是 <ins class="adsbygoogle"> + pub-XXXX client id,完全两套。

怎么判断:console 看 securepubads... 请求返回 200 但广告服务器回 204 No Content。你 Google 账号下只有 AdSense 没有 Ad Manager。

修复:要么在 Ad Manager 把 AdSense 接成 demand source(先开 Ad Manager,再 link 账号),要么继续用 AdSense <ins>不要同一页面混用 AdSense Auto Ads 和 GPT——它们会抢库存。

最小可用 GPT 模板(可直接复制)

<head>
  <script async src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
  <script>
    window.googletag = window.googletag || { cmd: [] };
    googletag.cmd.push(function() {
      googletag
        .defineSlot('/NETWORK_CODE/leaderboard', [[728, 90], [970, 90]], 'div-gpt-ad-leaderboard')
        .addService(googletag.pubads());
      googletag.pubads().enableSingleRequest();
      googletag.pubads().collapseEmptyDivs();
      googletag.enableServices();
    });
  </script>
</head>
<body>
  <div id="div-gpt-ad-leaderboard" style="min-width: 728px; min-height: 90px;">
    <script>
      googletag.cmd.push(function() { googletag.display('div-gpt-ad-leaderboard'); });
    </script>
  </div>
</body>

NETWORK_CODE 换成你 Ad Manager 的 network code;collapseEmptyDivs() 让填不到广告的 slot 自动塌缩,避免一大块空。

最短修复路径

按命中率排序:

  1. DevTools Network → 确认 gpt.js 真的加载了 → 没加载就是广告拦截 / CSP / 网络
  2. Console:googletag.pubads().getSlots() → 空说明 defineSlot 没跑;非空进下一步
  3. <div id>defineSlot(...)display(...) 三个 id 完全对齐 → 修掉绝大多数「脚本加载了但空着」
  4. 每个调用都包进 googletag.cmd.push(function(){ ... }) → 修掉时序竞争
  5. SPA 路由切换用 refresh() 而不是 redefine → 修掉「首页有广告后续页都空」

预防建议

  • 一个站只用一个广告系统:AdSense <ins> 或 Ad Manager GPT,永远别混。
  • slot 在 app 根定义一次,路由切换调 refresh()
  • dev 模式加个小 console banner 显示 slot 数和填充率,QA 时空 slot 一目了然。
  • 每个 slot 的 <div>min-width / min-height 等于你定义的最小尺寸——防 CLS,空 slot 也能直观看到。
  • 测试前用无痕窗口、关掉所有扩展,再下结论说「slot 坏了」。

FAQ

Q:GPT 能用 AdSense 的广告位吗? A:能——在 Ad Manager 里把 AdSense 接成 backfill demand source。页面上挂的是 GPT,Ad Manager 没匹配 line item 时 AdSense 兜底填充。

Q:为什么用 GPT 而不是 AdSense? A:GPT 给你直销 line item、广告调度、header bidding、人群定向、频次封顶;AdSense 是设完不管的产品;GPT 适合主动管理 demand 的发布者。

Q:本地 OK 生产挂。 A:三种可能:(a)生产 CSP 挡了 Google 广告域名;(b)忘了把 staging 的 network code 改成生产的;(c)生产域名没在 Ad Manager 站点白名单。看 console 具体错。

Q:googletag.pubads().getSlots() 有 slot 但还是空的。 A:库存问题不是代码问题。要么没有 line item 定向到这个广告位路径,要么账号太新还没 demand。去 Ad Manager → Delivery → Line Items 查匹配。

Q:同一页第一个 slot 填了,第二个空着。 A:基本是漏了 enableSingleRequest(),第二个 slot 请求在抢第一个;在 enableServices() 前加上 googletag.pubads().enableSingleRequest()。或者第二个 slot 你 defineSlot 了但忘 googletag.display(secondDivId)

相关阅读

标签: #AdSense #变现 #排查 #GPT Tag