很多站点上线 CDN 之后,会默认一件事:同一个 URL,不管谁来访问,看到的都是同一份页面。实际不是。蜘蛛从不同出口、不同地区发起请求,落到不同边缘节点,缓存命中情况、回源时机、源站当时的健康状态都可能不一样。结果就是,同一个 URL 在蜘蛛那边可能出现过好几个版本,而你自己访问时看到的一直是正常的那一个。
缓存键决定蜘蛛拿到哪一份
CDN 判断要不要用缓存,靠的是缓存键(cache key)。默认通常只包含 host、path 和 query,但很多配置会加入更多维度:设备类型、语言、Cookie、请求头。这些维度一旦和蜘蛛的请求头对不上,就会出现两种情况——要么蜘蛛每次都绕过缓存回源,要么蜘蛛拿到了一份给普通浏览器准备的缓存副本。
- 按 UA 分缓存:移动蜘蛛和桌面蜘蛛命中不同副本,页面里的链接可能不一样。
- 按 Cookie 分缓存:蜘蛛不带 Cookie,容易命中未登录版本,可能缺少入口类链接。
- 按地域分缓存:不同节点回源到不同源站或机房,内容发布时间有先后。
Vary 响应头会放大这个问题。一旦 Vary 里写了 User-Agent 或 Cookie,缓存会被切得非常碎,命中率下降,回源请求明显增加。对蜘蛛来说,抓取时延变大,同一批 URL 的抓取进度会被拖慢。
缓存没命中时,边缘回源会遇到什么
蜘蛛访问的往往是冷门 URL,缓存命中率比真实用户低得多,基本每次都要触发回源。这时候源站的真实状态才会暴露出来:
- 源站慢:边缘等待超时,可能直接返回 5xx 或旧副本,蜘蛛收到的是失败或不完整页面。
- 源站限流:蜘蛛的并发请求被挡掉一部分,返回 429 或 503。
- 健康检查误判:节点被摘除,部分请求落到兜底页或错误页。
这些情况在浏览器里不一定看得出来,因为你访问的大多是热页面,缓存里有现成副本。要看清楚,得对比源站日志和 CDN 日志:CDN 日志里的状态码分布,往往比源站日志更能反映蜘蛛实际收到了什么。
边缘节点之间很难完全一致
多节点部署时,缓存过期时间、预热策略、发布顺序都可能不同。一次内容更新之后,有的节点已经回源拿到新页面,有的还在返回旧副本,中间可能持续几分钟到几十分钟。蜘蛛在这段时间里抓取,就会同时看到新旧两个版本,链接集合也可能不一样。
判断标准不是“我这边打开正常”,而是不同节点、不同 UA、不带 Cookie 时返回的内容是否一致。
动手排查的几条路子
- 用蜘蛛 UA 和不带 Cookie 的请求,对同一 URL 多次请求,比较响应内容与响应头,重点看 Age、X-Cache 这类字段。
- 把请求打到不同地区节点,对比页面里的链接数量和主要结构。
- 核对 CDN 日志与源站日志:同一 URL 的请求量、状态码、响应时间是否对得上。
- 对需要抓取的页面明确缓存策略:静态页给较长的 max-age,列表页和详情页避免按 UA 切缓存。
- 排除机器人识别规则:确认蜘蛛 UA 不会被误判为异常流量而返回验证页。
配置上可以稳住几件事
第一,尽量让蜘蛛走和普通用户一样的缓存逻辑,少按 UA、Cookie 分叉。第二,源站对蜘蛛常用的抓取路径保持稳定响应,不要动辄 5xx。第三,内容发布后主动预热关键节点,减少新旧版本并存的时间。第四,把 CDN 的兜底页设成明确的状态码,不要用 200 返回一个加载中的空壳,那会让蜘蛛把空页面当成正常内容记下来。
这些调整不会直接带来收录,但能减少“蜘蛛看到的和你看到的不一样”这类无谓损耗。抓取本身是件需要确定性的工作,变量越少,越容易判断问题出在哪一步。