AdSense 状态写着 “Approved”,代码也贴了,文章页里该出现广告的位置就是一块空白。大部分人第一反应是代码错了,但代码错的概率大约只占 20%。真正的元凶通常是:AdSense 还在给你新 slot 暖机、slot 拿不到可填充的展示、或者页面缺少某个 Google 需要的信号。
下面按”在真实场景里出错的顺序”逐条排查。
常见原因
按命中率从高到低。
1. 通过审核后的最初 24-48 小时——slot 还在暖机
审核通过后,AdSense 大约要 1-2 天才会开始填充新 slot。后台状态是 “Approved”,但拍卖系统还没启动你这块库存。
怎么判断:DevTools 检查 ad slot 那段 DOM。会看到 <ins class="adsbygoogle" data-ad-status="unfilled">。如果你通过审核还不到 48 小时,就是暖机,等。
2. ads.txt 缺失或错
如果 https://yourdomain.com/ads.txt 不存在、或里面的 publisher ID 写错,AdSense 不会投放。Search Console 和 AdSense 都会提示,但容易被忽略。
怎么判断:
curl -s "https://yourdomain.com/ads.txt"
应该看到:
google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0
pub-ID 必须跟你 AdSense 账号完全一致。文件缺、字段多/少、ID 错——都不投。
3. ad slot 没有可填充的宽度
AdSense responsive 广告要求父容器在脚本执行时已经有非 0 的宽度。如果广告位被塞进 display:none 的块、宽度为 0 的侧栏、隐藏的移动端抽屉里,单元就会返回 unfilled。
怎么判断:DevTools 选中 <ins class="adsbygoogle">,看计算样式。宽度是 0px 就找父容器加个真实宽度。
4. ad slot ID 拼错
常见复制粘贴错误:data-ad-slot="1234567890" 跟你在 AdSense → Ads → By ad unit 里创建的任何 slot 都对不上。AdSense 不会报错,就是静默不投。
怎么判断:AdSense → Ads → By ad unit 列出所有 ad units。把你代码里所有 data-ad-slot 值跟它对照。任何对不上都不投。
5. 页面主题暂时没有广告主
“信用卡”页面的广告主需求大约是”古董打字机修复”的 5 倍。极冷门主题的页面,某段时间内可能真的没有可填充竞价。
怎么判断:把同一份代码贴到首页(主题更宽)。如果首页填充、冷门文章不填,是库存问题不是代码问题。
6. AdSense 检测到 “invalid traffic” 已经静默关停
如果你一直 F5 自己的页面、点自己的广告、或者有 bot 流量,AdSense 为了保护广告主,会悄悄禁掉你站点的投放——情况严重前不会发邮件。
怎么判断:AdSense → Policy center 看有没有 “Invalid traffic” 的提示。哪怕没有正式警告,可疑流量模式也会让填充率默默下降。
7. CMP(同意管理平台)把页面拦了
如果页面前面接了一个 CMP,用户没点同意,AdSense 会尊重这道关——不投个性化广告,某些配置下连非个性化也不投。
怎么判断:无痕模式打开页面,同意所有 cookie。广告出现了就是 CMP 在拦。决定一下默认策略:不同意的人也投上下文(非个性化)广告,还是完全不投。
最短修复路径
第 1 步:读 ad slot 的 data-ad-status
打开页面 → 检查广告占位 → 看 <ins> 标签:
data-ad-status 值 | 含义 | 怎么修 |
|---|---|---|
unfilled | slot 加载了但没拿到广告 | 看库存(#5)、CMP(#7)或等 |
| 整个属性都没有 | 脚本没在这个 slot 上执行 | 代码错或者 display:none |
filled | 广告投出来了(但可能被下游拦了) | 浏览器广告拦截、CSP、iframe sandbox |
这一个属性就排除掉一半诊断分支。
第 2 步:验证 ads.txt
curl -s "https://yourdomain.com/ads.txt"
diff <(curl -s "https://yourdomain.com/ads.txt") <(echo "google.com, pub-YOURID, DIRECT, f08c47fec0942fa0")
为空或不一致就在站点根目录建/修。Astro:public/ads.txt。Next.js:public/ads.txt。Vercel 一样。改完等 AdSense 最多 24 小时重爬。
第 3 步:确认 ad unit 存在且 slot ID 对得上
AdSense → Ads → By ad unit 复制 slot ID。在代码里搜:
grep -rn "data-ad-slot" src/
每个值都要能对上一个 AdSense ad unit。没用的代码片段删掉。
第 4 步:给 slot 一个可填充的宽度
如果 slot 在某些断点会塌掉的 flex / grid 里:
.ad-slot {
display: block;
min-width: 250px; /* AdSense 最小宽度 */
min-height: 100px;
}
或者把 <ins> 套一个 width: 100%; min-height: 250px 的 div。
第 5 步:关掉个性化数据再测
无痕打开 → CMP 选拒绝 → 看广告还出不出。一个都不出,说明 CMP 把”未同意者”全拦了。把默认改为非个性化广告:
<script>
(adsbygoogle = window.adsbygoogle || []).requestNonPersonalizedAds = 1;
</script>
第 6 步:修完等 48 小时
AdSense 的对账不是即时的。代码或 ads.txt 改完,给 24-48 小时再来下结论。
预防建议
- CI 里加
ads.txt校验:curl -sf yourdomain.com/ads.txt | grep -q "$PUB_ID"。 - 构建期校验所有
data-ad-slot值都在已知列表里。 - 不要把广告 slot 塞进会被条件隐藏的容器;占位永远渲染,要隐藏只通过 publisher 控制。
- 不要反复刷自己页面——会触发 invalid traffic 标记。
- 确认 CMP 至少允许”未同意者”看到非个性化广告。