如果两个 URL 返回的 HTML 正文逐字相同,但响应头不同,Google 仍可能把它们当作不同的抓取对象。影响最大的不是“内容是否重复”这一层,而是抓取预算、规范化信号、缓存与条件请求、以及索引状态诊断这四项判断。最容易被忽略的是:响应头先于正文被处理,它决定了 Googlebot 是否值得继续解析正文。
常见的情形是:同一套模板生成的页面,A 版返回 200 OK 且带 Content-Type: text/html; charset=utf-8,B 版因为落在某个中间层后面,返回 200 OK 但 Content-Type 缺失或写成 text/plain,或者带上了 X-Robots-Tag: noindex。正文抓下来一模一样,但 B 版长期不进入索引,或者进了又消失。用户按“内容重复”去改正文、加 canonical,往往没有效果,因为问题不在正文层。
解释一:Google 判定为重复内容,选择其中一个作为规范版本。这种情况下,两个 URL 都会被正常抓取和解析,只是 Google 在规范化时合并了信号。它的特征是:两个 URL 都可能出现在 site: 查询里,其中一个被标为“已选择规范网址”,另一个是“重复网页,Google 选择的规范网页与用户指定的不同”。
解释二:响应头让 Google 在解析正文前就改变了行为。比如 X-Robots-Tag: noindex、错误的 Content-Type、Vary 配置导致缓存命中错误版本、或者 Cache-Control 让 Googlebot 反复取到旧响应。特征是:正文相同的两个 URL,一个被抓取后正常进入索引,另一个在抓取统计里显示已抓取但从未编入索引,且抓取频次异常低或异常高。
先看 URL 检查工具里的“网页抓取”结果。它会显示 Googlebot 实际收到的 HTTP 响应头。如果这里出现 noindex、非 HTML 的 Content-Type,或者返回码与你在浏览器看到的不一致,那基本落在解释二。注意:URL 检查工具用的是实时抓取,可能与 Googlebot 实际抓取时收到的响应不同,所以还要对照服务器日志。
再看服务器日志里的响应头记录。如果日志显示 Googlebot 请求同一路径时,不同时间拿到不同响应头(例如缓存层有时补上 Content-Type,有时没有),说明中间层在按 UA 或按缓存状态返回不同头。这类差异无法从正文比对中发现。
最后看索引覆盖报告的分类。如果 B 版被归入“已抓取——尚未编入索引”,且正文与已索引的 A 版完全一致,优先怀疑响应头或渲染资源被拦截;如果 B 版被归入“重复网页”,则更可能是规范化判断。两者的处理动作完全不同:前者要修响应头,后者要处理 canonical 和内部链接。
假设某站点有 /p?id=1 和 /p/1 两个 URL,正文相同。/p?id=1 由应用服务器直接返回,带完整 Content-Type;/p/1 经过 CDN,CDN 对无扩展名路径默认返回 Content-Type: application/octet-stream。Googlebot 抓到 /p/1 时,可能因为类型不是 HTML 而放弃解析正文,于是这个 URL 长期不进入索引。
此时的动作是:在 CDN 层为 HTML 路径显式设置 Content-Type: text/html; charset=utf-8,并确认 X-Robots-Tag 没有被误加。改完后重新用 URL 检查工具请求 /p/1,确认 Googlebot 看到的响应头已变为 HTML 类型。如果之后该 URL 从“已抓取——尚未编入索引”转为可索引状态,说明响应头是主因;如果仍然不索引,再回头检查 canonical 和内部链接,因为此时响应头这一层已经被排除。这个顺序能避免在正文层反复做无用修改。
X-Robots-Tag 的 noindex 与 robots.txt 的抓取限制不是一回事。robots.txt 只阻止抓取,不保证移除已索引的 URL;而响应头里的 noindex 需要 Googlebot 能抓到该响应才能生效。Content-Type 影响解析,X-Robots-Tag 影响索引指令,Cache-Control 和 Vary 影响 Googlebot 拿到哪个版本,Link 头可能传递 canonical 信号。它们不是同一类问题,不能只改一个就假定全部解决。判断的落脚点始终是:先确认 Googlebot 实际收到的响应头,再决定是修头、修规范化,还是修内部链接;跳过响应头直接改正文,通常不会改变索引结果。