首页 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 = 0 | CSS 把 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: none、width: 0、height: 0,或者在没展开的 tab/accordion 里。首页用的是另一种父结构,所以那边正常渲染。AdSense 会做宽度校验:响应式 <ins> 量不到可用宽度时它拒绝填充,控制台会打出 adsbygoogle.js 的校验错误,比如 No slot size for availableWidth=0。
怎么判断: 文章页选中 <ins class="adsbygoogle"> → Computed → 看 width 和 height。是 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.com、doubleclick.net 的 iframe sandbox 错误。你的 CSP 必须在 script-src 和 frame-src 里放行这些域名。
最短修复路径
第 1 步:确认文章页真的发出了广告请求
打开文章,按 F12,切到 Network,过滤框输入 ads?(这是 Google 的官方 DevTools 检查法)。
- 出现以
ads?client开头的请求 = 代码跑了、也请求了广告。看它的状态码:200但 slot 空白 → ads.txt 或内容/unit 问题,继续第 4、第 5 步。403→ Google 拒绝向这页投放(账号/站点级拦截、政策、或 unit 不存在)。查 AdSense → Sites 和 Policy 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 > 0 | unfilled,w/h = 0 | 文章 CSS 把 slot 塌了(原因 2) |
filled,w/h > 0 | undefined,w/h > 0 | 文章模板里 push() 没跑(原因 5) |
filled,w/h > 0 | unfilled,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 从页面上看不到的。
怎么确认修好了
- 关掉广告拦截器,硬刷新(
Cmd/Ctrl+Shift+R)文章。 - Network 过滤
ads?,看到一条200的ads?client请求。 document.querySelector('ins.adsbygoogle').getAttribute('data-ad-status')返回filled。- 在至少 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 和账号里的投放排障器。