很多人看抓取日志时只盯着状态码和 URL,却忽略了一个中间层:CDN 或反向代理。蜘蛛的请求通常不会直接落到源站,而是先到边缘节点。如果缓存命中,蜘蛛拿到的是一份已经存在的副本,而不是源站刚刚生成的新页面。对蜘蛛来说,响应同样是 200,但内容和新鲜度可能已经不同。
蜘蛛抓取经过缓存时发生了什么
一个正常的抓取请求大致会走这样的路径:蜘蛛发起请求,DNS 解析到 CDN 节点,节点检查本地是否有可用缓存。有则直接返回,没有则回源,源站返回后再按缓存规则决定是否存下来。整个过程对蜘蛛是透明的,它不会区分“这是缓存版”还是“这是源站版”。
问题在于,缓存有生命周期。源站更新了标题、正文或结构化数据,缓存副本却可能还是几小时前甚至几天前的版本。蜘蛛如果在这段时间抓取,看到的就是旧内容。它不会因为源站已经更新就自动再抓一次,下一次刷新取决于缓存过期和蜘蛛自己的调度。
响应头里藏着缓存线索
Age 与 X-Cache
Age 表示这份响应在缓存里已经存了多久。Age 为 0 或很小,说明大概率是刚回源或刚写入缓存;Age 很大,说明蜘蛛拿到的是一份旧副本。X-Cache、CF-Cache-Status 这类自定义头也常用 HIT、MISS、EXPIRED 来告诉你命中情况。排查时可以先看这些头,再决定是不是缓存导致蜘蛛看到旧内容。
Cache-Control 与 s-maxage
Cache-Control 里的 max-age 和 s-maxage 决定了缓存能存多久。s-maxage 专门作用于共享缓存,也就是 CDN 这类节点。如果 HTML 页面设了很长的 s-maxage,源站更新后蜘蛛仍可能持续拿到旧版。静态资源可以长缓存,但 HTML 通常需要更短的有效期,或者依赖主动刷新。
Vary 头会分裂缓存
如果响应里带 Vary: User-Agent,CDN 会按 User-Agent 分别缓存。蜘蛛的 UA 和普通用户不同,可能命中一个独立的缓存桶,或者完全绕过缓存。前者容易让蜘蛛长期看到某个特定版本,后者则让缓存失去减轻源站压力的意义。还有 Vary: Cookie 的情况,处理不好会出现缓存污染。
哪些缓存设置容易挡路
- HTML 页面缓存时间过长,更新后蜘蛛仍读到旧内容。
- 把 404、410 或重定向响应也缓存下来,错误状态被反复返回。
- 按 UA 或 Cookie 分裂缓存,蜘蛛和用户看到不同版本。
- 缓存了带参数 URL,筛选页、排序页被大量存储,抓取时命中空壳。
- 源站已经删除页面,缓存仍返回 200,形成事实上的软 404 残留。
怎么排查缓存与源站不一致
- 用蜘蛛的 User-Agent 请求目标 URL,记录响应头和正文摘要。
- 直接请求源站 IP 或绕过 CDN,对比同一 URL 的响应头和正文。
- 看 Age 和命中状态,判断这次抓取是 HIT 还是回源。
- 检查 Cache-Control、Vary、Expires,确认 HTML 的缓存策略是否合理。
- 更新重要页面后主动刷新缓存,再观察蜘蛛下一次抓取拿到什么。
- 对经常变动的列表页、详情页设置较短 TTL,或采用按需刷新。
排查时不要只看一次结果。缓存命中具有随机性,多节点、多地区可能表现不同。要在日志或监控里持续观察,而不是靠单次 curl 下结论。
缓存能减压,但不能替代抓取策略
缓存命中可以减少源站压力,让服务器在蜘蛛集中抓取时更稳。这一点对站点运营是好事。但缓存不会改变蜘蛛是否继续抓、是否索引,也不会自动让新 URL 被发现。抓取预算、内链路径、Sitemap 和内容质量仍然是基础。把缓存当成“让蜘蛛多抓”的手段,容易失望。
让蜘蛛看到和用户一致、足够新的页面版本,比追求缓存全命中更重要。
日常可以做的,是保证 HTML 缓存不过期到失真,静态资源放心长缓存,重要更新主动刷新,并定期用蜘蛛视角检查响应头。这样既减轻服务器负担,也不会让蜘蛛长期停在旧版本上。