首页有广告、文章页空白:6 类原因 + DevTools 排查修复

首页 AdSense 正常、文章页空白,多半只是两个模板的差异:漏装脚本、容器塌成 0、ad-slot ID 不对、或内容太薄。一套 DevTools 左右对比法,几十秒定位是哪一类。

首页 AdSense 广告显示正常,点进文章却是空白广告位。一样的 ad-unit ID、一样的域名、一样通过审核的账号。这就是”不同模板”故障模式,绝大多数不是 AdSense 的 bug。是两个模板之间有结构性差异:漏装了 script 标签、某条 CSS 把 slot 塌成 0、data-ad-slot 值不对,或者单篇文章内容薄到不达标、而首页因为聚合内容达标。

最快的判断: 在 Chrome 里打开文章,按 F12,切到 Network 标签,过滤框里输入 ads?。如果看不到任何以 ads?client 开头的请求,说明文章模板根本没加载或没触发 AdSense 代码——这是下面的原因 1 或 5,解决办法是把脚本和 push() 收进同一个共享 layout。如果请求在、但返回 403 或者 slot 是 data-ad-status="unfilled",那就是内容/政策或 ad-unit ID 的问题(原因 3、4、6)。

诊断关键是用 DevTools 把两个模板左右放一起对比,而不是反复看 AdSense 设置。旧教程推荐的 Google Publisher Toolbar 扩展早在 2020 年就下架了——截至 2026 年 6 月,官方支持的工具就是 Chrome DevTools 加上账号里的投放排障器。

先做 30 秒分桶

文章页里你看到的现象大概率原因跳到
Network 里没有 ads?client 请求文章模板缺脚本 / 缺 push()原因 1 / 5
请求在,但 <ins>width/height = 0CSS 把 slot 塌了原因 2
请求返回 403账号/站点级投放被拦(ads.txt、政策)原因 3 / 4
正常尺寸的 slot 显示 data-ad-status="unfilled"内容太薄或 ad-unit ID 不对原因 4 / 6
控制台出现 ERR_BLOCKED_BY_CLIENT是你自己的广告拦截器,不是站点 bug不是你的问题

常见原因

按命中率从高到低。

1. AdSense 脚本只在首页加载、文章模板没装

常见场景:你把 AdSense 加载脚本加到了首页 layout,但文章用的是另一个 layout,没包含它。文章的 <ins> 标签在那里,但 adsbygoogle.js 从来没执行过。

怎么判断: DevTools → Network → 在文章页过滤框输入 ads?。如果没有任何以 ads?client 开头的请求,说明这页根本没发出广告请求。再过滤 adsbygoogle.js 看加载脚本本身——0 个结果就是文章模板里没有这个 script 标签。

2. 文章模板把 slot 包在塌掉的 CSS 容器里

slot 在 DOM 里,但父容器是 display: nonewidth: 0height: 0,或者在没展开的 tab/accordion 里。首页用的是另一种父结构,所以那边正常渲染。AdSense 会做宽度校验:响应式 <ins> 量不到可用宽度时它拒绝填充,控制台会打出 adsbygoogle.js 的校验错误,比如 No slot size for availableWidth=0

怎么判断: 文章页选中 <ins class="adsbygoogle">Computed → 看 widthheight。是 0 就是父容器塌了。跟首页版本对比。

3. ad-unit ID 不同——只有首页那个配对了

两个模板用了不同的 data-ad-slot 值。首页那个在 AdSense 里配好了;文章那个指向一个被删除的、或从未创建的 slot。请求一个不存在的 unit 可能直接返回 403,也可能一直 unfilled

怎么判断:

grep -rn "data-ad-slot" src/

把值跟 AdSense → Ads → By ad unit 对比。文章里的值必须对应一个存在且活跃的 unit。

4. 文章内容太薄、无法承载广告

AdSense 的计划政策以及”无发布商内容的屏幕投放”政策都限制在低价值或内容稀薄的页面投放。首页聚合了内容(最新文章、hero 文字、分类),所以过线;某些很短的单篇文章过不了线。官方没有公布硬性字数,但截至 2026 年 6 月,常见触发条件是页面可见区域过半为非原创、或正文不到约 300 字——这类文章 AdSense 会给 data-ad-status="unfilled",但首页照常投放。

怎么判断: 字数不到约 300、结构稀薄、原创正文很少。结合分桶确认:正常尺寸的 slot 连续多次刷新都是 unfilled、而其它(更长的)文章能填充,就指向这一类。

5. 文章模板在 slot 出现之前(或根本不)跑 push()

如果框架异步 hydrate 文章页,adsbygoogle.js<ins> 还没进 DOM 时就跑了 push({}),或者文章路由上 push() 压根没跑,slot 就一直空。SPA 路由切换是经典坑:首次加载正常,但客户端导航进文章后没重新跑 push()

怎么判断: 文章页控制台跑 window.adsbygoogle?.loaded。文章页是 undefined、首页有值,说明这个模板没 push。用 Astro view transitions 或 Next.js 客户端导航时,再直接硬刷新文章 URL——如果硬刷新能填、软导航不能,就是 push 没绑到路由切换上。

6. CSP 或 iframe sandbox 挡住文章模板的广告

文章模板用了更严的 Content Security Policy,或者把内容包在 <iframe sandbox> 里,AdSense 就无法注入自己的 frame。

怎么判断: 文章页 DevTools Console。找 Refused to load / Refused to frame 这类 CSP 错误,或提到 googlesyndication.comdoubleclick.netiframe sandbox 错误。你的 CSP 必须在 script-srcframe-src 里放行这些域名。

最短修复路径

第 1 步:确认文章页真的发出了广告请求

打开文章,按 F12,切到 Network,过滤框输入 ads?(这是 Google 的官方 DevTools 检查法)。

  • 出现以 ads?client 开头的请求 = 代码跑了、也请求了广告。看它的状态码:
    • 200 但 slot 空白 → ads.txt 或内容/unit 问题,继续第 4、第 5 步。
    • 403 → Google 拒绝向这页投放(账号/站点级拦截、政策、或 unit 不存在)。查 AdSense → SitesPolicy center
  • 没有 ads?client 请求 → 脚本在这页没跑。继续第 2 步。

第 2 步:确认两页都有加载脚本

// 在每页的 DevTools Console 里跑
document.querySelectorAll('script[src*="adsbygoogle"]').length
// 两页都应该是 1

文章页是 0 就是模板没装加载脚本。挪进一个共享 layout 组件,让所有页面继承。

第 3 步:对比 <ins> 的计算尺寸和状态

// 在每页的 Console 里跑
Array.from(document.querySelectorAll('ins.adsbygoogle'))
  .map(el => ({ status: el.getAttribute('data-ad-status'), w: el.offsetWidth, h: el.offsetHeight }));

data-ad-status 取值有 filled(已投放)、unfilled(没返回广告,留空白)、unfill-optimized(开了填充优化、有意留空)。详见官方说明

首页文章诊断
filled,w/h > 0unfilled,w/h = 0文章 CSS 把 slot 塌了(原因 2)
filled,w/h > 0undefined,w/h > 0文章模板里 push() 没跑(原因 5)
filled,w/h > 0unfilled,w/h > 0内容薄或 ad-unit ID 不对(原因 4 / 6)

第 4 步:确认 push() 在 slot 出现之后才跑

script 标签必须存在 push() 必须在 <ins> 进 DOM 之后、每个 slot 跑一次。SSR / 静态标记:

<ins class="adsbygoogle" ...></ins>
<script>(adsbygoogle = window.adsbygoogle || []).push({});</script>

SPA 路由切换(Next.js、Astro view transitions):每次导航后对新挂载的 <ins> 各跑一次 push({})。同一个 slot push 两次,控制台会报 adsbygoogle.push() error: All ins elements ... already have ads in them.

第 5 步:验证 ad-unit ID 对得上

grep -rn "data-ad-slot" src/ | sort -u

打开 AdSense → Ads → By ad unit。上面每个 data-ad-slot 值都应对应一个活跃 unit。打错字或 unit 被删,会导致 403 或永久 unfilled

第 6 步:换一篇文章测

如果只有一篇有问题、其它能填充,就是那篇太薄(原因 4)。加约 500 字有分量的原创内容。

所有文章都有问题就是模板级(原因 1、2、3、5)——改共享 layout,不是改单篇文章。

第 7 步:跑账号里的投放排障器

在 AdSense 里打开 Help → Troubleshoot → “Ads not showing”(即投放排障器)。它会扫描账号级投放问题——站点没添加/没验证、政策处罚、ads.txt 问题——这些是 DevTools 从页面上看不到的。

怎么确认修好了

  1. 关掉广告拦截器,硬刷新(Cmd/Ctrl+Shift+R)文章。
  2. Network 过滤 ads?,看到一条 200ads?client 请求。
  3. document.querySelector('ins.adsbygoogle').getAttribute('data-ad-status') 返回 filled
  4. 在至少 3 个不同文章 URL 上重复验证,其中一个要通过客户端导航进入(不只是硬加载)。

哪些情况可能不是你操作错了

AdSense 填充率一天内随广告主需求波动,10–30% 的波动是正常的,健康页偶尔出现一次空白也属正常。判断”出问题了”的阈值是”同一页连续几小时刷新都空白”,不是”刷一次空”。同理,你自己控制台里的 ERR_BLOCKED_BY_CLIENT 意味着浏览器的广告拦截器吃掉了请求——先用干净的浏览器配置文件测一遍,再下结论说是站点 bug。

容易误判的情况

“文章页广告不出来”常被当成 AdSense 宕机或账号问题。但只要同一账号的首页在投放,账号就是好的——差异几乎都来自模板结构、CSS、ad-unit ID 写错,或那一页内容太薄。

预防建议

  • 跨模板共用同一个 <AdSlot> 组件,不要复制粘贴 <ins> 标签。
  • AdSense 加载脚本只放在一个根 layout 里,所有页面继承。
  • CI 里校验广告容器 CSS:每个 <ins> 的父容器宽度必须非 0。
  • 单页广告总面积控制在视口约 30% 以下;截至 2026 年 6 月,过密的页面会被静默地不投放。
  • 想变现的文章设最低字数门槛(约 500 字)。
  • 框架升级或路由改动后,至少在 3 个文章 URL 上重新验证广告,其中一个走软导航进入。

FAQ

  • 首页有广告、每篇文章都空白,是账号被封了吗? 不是。账号里只要有页面在投放,账号就在投放。站点级封禁连首页也会返回 403。只有文章空白说明是模板/内容差异。
  • Network 请求返回 200 但 slot 还是空,为什么? 200 却没广告,通常是 ads.txt 缺失或写错,或者页面太薄/低价值填不了。先在 你的域名/ads.txt 确认列了你的发布商 ID,再查内容厚度。
  • 填充率为什么波动? 广告主需求随内容、受众、地区、季节变化。一天内 10–30% 的波动是正常的。
  • 加更多广告位能提升填充吗? 只能到一定程度。位太多会稀释每个 slot;大多数细分领域,每篇 2–3 个手动广告位(可再叠加 Auto ads)是甜点。
  • 还需要装 Google Publisher Toolbar 扩展吗? 不需要——它 2020 年就下架了。改用 Chrome DevTools 和账号里的投放排障器。

相关阅读

标签: #AdSense #变现 #排查 #填充率