搜索抓取

缓存协商与 304:减少蜘蛛对同一 URL 的重复下载

蜘蛛对同一个 URL 反复抓取,往往比抓取不足更浪费预算。本文讲清条件请求(If-Modified-Since、If-None-Match)与 304 响应的实际作用,说明哪些页面适合依赖缓存校验、哪些内容更新反而会被 304 挡住,以及 Sitemap 的 lastmod 该怎么和缓存头对齐,并给出可落地的验证方法。

搜索抓取

缓存协商与 304:减少蜘蛛对同一 URL 的重复下载

蜘蛛抓取站点时,最容易被忽略的浪费不是“抓得少”,而是“抓重复”。同一个 URL 在几周内被反复访问,服务器每次都把整份 HTML 重新吐一遍,带宽、CPU 和抓取配额都在为此买单。缓存协商(条件请求)正是为这种场景准备的:让蜘蛛问一句“变了没有”,没变就只回一个 304。

蜘蛛为什么会重复访问同一个 URL

蜘蛛不会完整记住页面内容,它保存的是上次抓取的时间、ETag 之类的校验信息。再次访问时,它会把这些信息带在请求头里。如果你的服务器不做校验,直接返回 200 和完整正文,蜘蛛只能重新下载、重新解析。

对更新缓慢的页面——关于我们、帮助文档、半年前的文章——这类重复下载会占据相当比例。它们不是没有价值,而是没有必要每次都整份重来。

条件请求是怎么工作的

  1. 蜘蛛第一次抓取,服务器在响应头返回 Last-Modified 或 ETag。
  2. 下次抓取时,蜘蛛带上 If-Modified-Since 或 If-None-Match。
  3. 服务器比对后,如果内容没变,返回 304 Not Modified,不带正文。
  4. 如果变了,正常返回 200 与新内容,同时更新校验标识。

对蜘蛛而言,304 表示“这个 URL 我看过,内容照旧”,这次访问仍会被记录,但省掉了下载和解析;对服务器而言,省掉的恰恰是最贵的部分——生成页面和传输正文。

什么情况不适合依赖 304

  • 内容确实改了,但 Last-Modified 没更新,例如只改了数据库字段,文件时间没动。蜘蛛会持续拿到 304,看不到更新。
  • 页面由模板拼装,每次生成的 ETag 都不一样,等于每次都“变了”,304 永远不会命中。
  • 页面包含实时数据、评论数或库存,几分钟就变一次,强行 304 反而会让缓存判断失真。

判断标准很简单:这个页面的“变”是内容层面的,还是渲染层面的。只有内容层面的变化才值得用缓存头表达。

Sitemap 的 lastmod 要和实际对齐

Sitemap 里的 lastmod 与响应头的 Last-Modified 描述的是同一件事的不同侧面,最好保持一致。如果 Sitemap 声明“昨天更新”,而服务器对条件请求返回 304,蜘蛛会收到互相矛盾的信号:要么怀疑 Sitemap 不准确,要么降低对 lastmod 的信任。

反过来,如果内容真的更新了,Sitemap 的 lastmod 却一直不动,蜘蛛也可能按旧的节奏来,更新的发现会变慢。

配置时容易踩的坑

  • CDN 与源站的缓存头不一致,蜘蛛看到的是边缘节点的响应,源站的校验信息被覆盖。
  • ETag 里混入了随机值、时间戳或进程 ID,导致同一页面每次校验值都不同。
  • 只关注状态码,不看日志里同一 URL 的 200 与 304 比例,改没改,全凭感觉。
  • 把动态参数页也纳入长期缓存,结果参数变了内容却返回 304。

怎么验证有没有生效

  1. 用抓取工具或命令行带 If-Modified-Since 请求同一 URL,确认第二次返回 304。
  2. 在服务器日志里按 URL 统计 200 与 304 的次数,看重复下载是否下降。
  3. 对确认更新过的页面,验证服务器是否及时返回 200 与新内容,而不是继续 304。
缓存协商并不决定蜘蛛要不要抓,它决定的是每次抓取的成本。抓取预算有限时,把重复下载省下来,才等于把机会留给了真正的新 URL。