SEOAdSenseads.txtCloudflareNext.js

我的 ads.txt 验证之路:与 AdSense 审核员博弈的 72 小时

2026-04-039 分钟阅读

面对 AdSense 后台那明晃晃的红色警告:“ads.txt 未找到”,而明明文件就在根目录下,这到底是怎么回事?复盘如何解决静态站点的爬虫校验难题。

1. 糟糕的开头

凌晨三点,当我第六次看到 Google AdSense 后台那个该死的红色感叹号“ads.txt 未找到”时,我整个人都裂开了。明明文件就在网站根目录下,我自己用浏览器访问秒开,命令行 curl -I 也是干脆利落的 200 OK。可那些审核蜘蛛就像得了白内障一样,视而不见。这种感觉就像你数学考试明明写对了答案,但监考老师非说你没动笔一样抓狂。

2. 我的思考

作为一名 00 后开发者,我一直在给 daima.life 卷性能,用了 Cloudflare Pages + Next.js 的黄金组合。但这套“全栈边缘计算”架构太前卫了,默认的 308 重定向和目录标准化(Trailing Slash)对于那些只认老旧协议的爬虫来说,这简直是灾难。我意识到,市面上现成的 ads.txt 生成器太简陋了,它们只管生成内容,不管部署环境。于是我决定在 daima.life 自家搓一个针对现代静态平台的 ads.txt 工具,顺便把这里的绕弯路经验彻底“避坑”公示。

3. 技术硬核区:规避 308 重定向的陷阱

在 Next.js 配置里,如果开启了 trailingSlash: true,那么所有以文件结尾的请求都会经历一次从 /ads.txt/ads.txt/ 的跳转。对浏览器来说这没问题,但 AdSense 的蜘蛛极其保守,很多时候它碰到 308 跳转就直接判定失败。为了解决这个问题,我们需要在 next.config.mjs 里精准控制静态文件的暴露逻辑。

// 避坑核心逻辑:确保文本文件不被 Trailing Slash 坑死
// 我们需要在构建 manifest 中显式定义这些静态路径
export default {
  // 强制不跳转静态文本文件
  async headers() {
    return [
      {
        source: '/ads.txt',
        headers: [
          {
            key: 'Content-Type',
            value: 'text/plain; charset=utf-8',
          },
          {
            key: 'Cache-Control',
            value: 'public, max-age=0, s-maxage=3600', // 给爬虫留点余地,但别卡太死
          },
        ],
      },
    ];
  },
};

此外,我还用正则表达式重写了 Cloudflare 的 WAF 规则。因为有些爬虫的 User-Agent 实在太像恶意采集器,默认会被 Cloudflare 的高安全级别配置给“幽灵屏蔽”。我专门为 AdSense 蜘蛛开了一道绿色通道,通过验证其原始 ASN 和 IP 段,确保它们在通过边缘节点时是“秒开”状态,没有任何质询弹窗。

4. FAQ 模块

Q1: 为什么手动访问行,但后台显示“未找到”?

A: 查查 IPv6。Cloudflare 默认开启 IPv6 支持,但如果你的 DNS 解析或者 AAAA 记录配置有误,爬虫走 IPv6 路径可能会超时。建议通过 curl -6 自行模拟验证一次。

Q2: 已经改好了,为什么状态还是红色?

A: 缓存权重。AdSense 蜘蛛的更新周期是 72 小时。即使你改对了,也得去 Cloudflare 后台手动执行一次 Global Purge (全量刷新),确保全球 280 多个边缘节点吐出来的都是最新冷数据,否则蜘蛛抓到旧的 308 跳转依然算你挂。

Q3: 文件格式有什么容易被忽略的细节?

A: 别在 CONTACT= 或者 OWNER= 里用骚操作。严格遵循 IAB Tech Lab 标准。任何多余的制表符(Tabs)或者中文全角逗号,都会导致整行无法被解析。用我的 ads.txt 工具生成的绝对是“真香”标准的格式。

5. 结尾

折腾了 72 小时,我的看板终于变绿了。但这一波博弈让我意识到,所谓的“无服务器”和“静态化”背后隐藏着巨大的通信复杂性。下一步我打算在 daima.life 集成一个全球爬虫可达性测试工具,专门模拟各家蜘蛛的脾气。毕竟,在这个数据决定生死的时代,能被蜘蛛看懂你的心,也是一种硬核实力。