在大多数情况下,两个 URL 返回完全相同的 HTML 正文、但响应头不同,会让收录判断从“内容是否重复”转向“哪一个响应代表规范版本”。如果差异落在状态码、Content-Type、Content-Language、Vary 或缓存指令上,判断依据就不再是正文相似度,而是响应层给出的语义。反例是:当两套响应头都返回 200 且正文一致、又没有自引用 canonical 或 hreflang 指向时,仅凭响应头差异不足以断定哪一个是重复项。
响应头不是装饰。它至少承载三种与收录相关的信号:
301、302、404、410 决定这个响应是可用版本、临时跳转还是失效资源。Content-Type 的字符集、Content-Language 决定解析与语言归属。Vary、Cache-Control 决定同一 URL 在什么条件下返回不同表示。如果两套响应只在 Cache-Control 的 max-age 上有差别,正文和状态码一致,那么这通常不构成收录层面的版本冲突,只能说明缓存策略不同。若差异出现在 Vary 上,例如一套声明 Vary: Accept-Language、另一套没有,就要考虑同一 URL 是否被设计为按请求头返回不同语言版本。此时判断对象已经不是两个 URL,而是一个 URL 的多个表示。
会改变判断的差异,通常满足两个条件之一:它改变了响应的可用性,或它改变了资源的身份。具体来说:
Content-Language 不同,但两套正文完全一致。这时应优先核查页面内是否有 hreflang 或语言切换链接,而不是直接认定其中一套是重复内容。Content-Type 的 charset 不同。若一套声明 UTF-8、另一套声明其他字符集,解析结果可能出现乱码,收录判断应先解决解析正确性,再谈重复。Vary、另一套不带。这会影响中间缓存和抓取工具看到的表示是否稳定,进而影响对“同一 URL 是否只有一个版本”的判断。以上差异中,只有状态码和 Content-Language 更接近“身份”问题;缓存类头更多影响抓取与回访时看到的内容,而不是直接决定哪一版应被收录。
如果没有日志、没有抓取配额、也没有后台权限,仍然可以先做一步可验证的动作:用同一请求头组合分别请求两个 URL,只记录状态码、Content-Type、Content-Language、Vary 和正文是否一致。这个动作的产出是一张对照表,而不是结论。
对照表的作用是缩小范围。若差异只出现在缓存指令上,下一步应转向检查页面内的 canonical 是否自引用、是否有分页或筛选参数;若差异出现在 Content-Language 上,下一步应检查 hreflang 与语言切换链接是否互相指向;若一套返回 200、另一套返回 404,下一步应确认哪一个 URL 出现在站内链接和站点地图中。站点地图不保证收录,但它能说明站点希望哪个版本被优先发现。
这个动作的局限也要写明:它不能推出搜索引擎实际选择了哪一版,也不能推出抓取频率或索引状态。请求量、抓取量或某个状态码归零,都不能单独证明处理正确,因为缓存、请求头差异、网络中间层和抓取调度都可能造成同样的观测结果。
假设某站有 /a 和 /b 两个 URL,正文完全相同,都返回 200。唯一差别是 /a 返回 Content-Language: zh-CN,/b 返回 Content-Language: en,两页都没有 hreflang,也没有自引用 canonical。此时不能因为正文相同就断定其中一页是重复内容,因为响应头声明了不同的语言归属;但也不能仅凭这个头就认定两页应分别收录,因为缺少页面内的语言互指关系。可执行的下一步是:先给两页补上互相指向的 hreflang,再观察哪个版本在站内链接和站点地图中被引用。这个例子中的数字和 URL 只是用于说明比较方法,不代表任何真实站点数据。
使上述判断失效的反例是:两套响应头都返回 200、正文一致、Content-Language 也一致,唯一差别是 Cache-Control 的 max-age 不同。此时响应头差异不构成版本冲突,真正需要检查的是页面内 canonical、分页链接和站内导航是否指向了不同 URL。若把缓存差异当成重复信号处理,可能会错误地改动 canonical 或提交移除,反而影响正常的收录判断。
下一步动作应固定为:先记录对照表,再确认页面内是否存在自引用 canonical、hreflang 或分页关系;只有在页面内信号也指向冲突时,才考虑调整响应头或提交变更。robots.txt 的抓取限制不等于可靠的索引移除,因此不要用 Disallow 来替代版本选择。