爬虫判断“这是不是同一个 URL”,靠的是字符串本身;而服务器判断“这个请求该返回什么”,靠的是文件系统规则、重写规则和默认文档配置。当这两套口径不一致时,同一个页面就可能以多种写法被反复发现,日志里看起来入口很多,实际内容却只有一份。
差异通常从哪里来
- 服务器或容器镜像对路径大小写不敏感,/About 与 /about 都可能返回 200,正文完全一样。
- 重写规则或 try_files 自动补斜杠,/list 直接 301 到 /list/,但内链里两种写法混用。
- 模板不同位置手写的链接不一致,导航输出 /Product/,正文里却是 /product。
- 入口同时存在带与不带默认文档名的版本,例如 /a/ 与 /a/index.html。
- CDN 或反向代理做过路径归一化,源站日志看到的路径与爬虫实际请求的写法并不完全一致。
会带来什么影响
最直接的结果是抓取请求被分散到若干“看起来不同”的入口上,同一份内容被多次请求;其次是统计失真,你在日志里数出来的入口数、状态码分布,可能并不对应真实页面数量。如果 sitemap、canonical 和内链各用一种写法,后续做覆盖核对时也会对不上号。
自查顺序
- 先从访问日志里取样,把状态码为 200 的路径按小写形式分组,找出同一路径出现多种大小写写法的记录。
- 用 curl 分别请求 /About、/about、/About/,记录状态码、Location 响应头,以及返回正文的长度或 ETag 是否一致。
- 核对 sitemap、feed、内链模板中同一路径的写法是否统一,是否存在两套写法同时被抓。
- 检查服务器配置中的 rewrite、try_files、DirectorySlash 一类指令,确认是否已经存在归一化规则,规则之间是否互相冲突。
- 对比 canonical、og:url 里指向的形式,与页面的主要入口写法是否一致。
收敛处理思路
先选定一种规范形式,常见做法是小写、静态文件不带结尾斜杠、目录类保留斜杠,其余写法统一用 301 收敛过去。只靠 canonical 声明通常不够,因为它不减少爬虫对这几种写法的实际请求。
- 在服务器层做 301 归一化,比在每个页面里写标签更彻底。
- 内链、sitemap、分页组件同步改成同一形式,避免继续产生新入口。
- 301 的目标要直接落到最终地址,不要出现跳向另一个 301 的情况,否则会形成链条。
- 如果服务端本身区分大小写,可以直接对错误写法返回 404,但改动前要确认没有真实存在的混合大小写页面被误伤。
调整归一化规则之前,先抽一批真实 URL 做回归,尤其是带查询参数、带语言前缀的入口,避免把有效页面误重定向掉。
验证是否收敛
规则上线后,隔一段时间再按同样的方式抽样日志:看同一路径的小写分组里是否只剩一种写法,301 的命中量是否在下降并趋于稳定,sitemap 中被抓取的 URL 是否与内链输出形式一致。这个过程不需要大改,重点是持续抽样核对,比一次性全站替换更容易发现遗漏。