搜索抓取

蜘蛛回访时的缓存协商:304 省下的是成本,不是抓取次数

蜘蛛回访一个没变的页面,服务器要不要把整份 HTML 再传一遍?缓存协商决定了这次抓取的成本。本文讲清条件请求的触发方式、ETag 与 Last-Modified 的常见坑、304 能省下和不能省下的东西,以及怎么和 Sitemap 的更新时间信号对齐。

搜索抓取

蜘蛛回访时的缓存协商:304 省下的是成本,不是抓取次数

蜘蛛抓一个页面,往往不是一次性的。内容没变,它过几天还会再来一趟;变化频繁的页面,回来的间隔更短。对服务器来说,这些回访里绝大多数请求拿不到任何新信息,却要重新渲染一次页面。缓存协商就是为这种场景准备的:让服务器在内容没变时明确告诉蜘蛛“没变”,而不是把整份 HTML 再吐一遍。

条件请求是怎么发生的

蜘蛛手里存着上次抓取的结果,回访时会带上两个头里的一个或两个:If-Modified-Since(配合上次响应里的 Last-Modified)和 If-None-Match(配合 ETag)。服务器比对之后,如果内容没变,返回 304 Not Modified,不带正文;变了就正常返回 200 和新内容。

这个过程对蜘蛛是透明的,它发的是一个普通 GET,只是多带了条件头。服务器返回 304,蜘蛛就知道本地副本还能继续用。

304 省的是什么,不省的是什么

  • 省的是带宽和服务器渲染开销。大页面、需要查库拼装的页面,304 能省掉相当一部分成本。
  • 省的是蜘蛛处理页面的时间。没有正文要解析,这一轮的消耗更小。
  • 不省的是抓取次数。一次 304 依然是一次抓取请求,依然占用抓取配额。想让蜘蛛少来几趟,靠的是内容更新节奏和站点整体质量,不是 304。
304 是“这次别传了”,不是“以后别来了”。

什么时候返回 304 反而有风险

最常见的错误,是把 304 当成万能的省流开关:

  • 内容已经变了却返回 304。常见原因是 ETag 或 Last-Modified 没跟着内容更新,或者 CDN 缓存住了旧版本。蜘蛛会继续用旧快照,用户看到的却是新页面。
  • ETag 每次请求都变。有些服务器用进程 ID、时间戳甚至随机值生成 ETag,同一个 URL 每次返回都不一样,条件请求永远匹配不上,等于白设。
  • 把 304 当屏蔽手段。想让蜘蛛别抓某个页面,应该用 robots.txt 或 404/410,而不是对所有请求一律回 304。长期返回 304 而内容又对不上,只会让服务器和蜘蛛的信号互相打架。

配置上的几个稳妥做法

  1. 静态资源:给足缓存时间,配合 ETag 或 Last-Modified,让回访直接命中 304。
  2. 动态页面:如果内容变化由数据驱动,把 Last-Modified 绑定到真实的数据更新时间,而不是页面生成时间。
  3. ETag 尽量基于内容哈希,保证同一份内容在不同机器、不同进程下算出同样的值。
  4. 304 响应不要带正文,也不要在这类响应里改变头部语义,保持干净。
  5. CDN 与源站的缓存头保持一致,避免边缘节点和源站各自为政,一会儿 200 一会儿 304。

和 Sitemap、内链放在一起看

Sitemap 里的 lastmod、页面自身的 Last-Modified、实际内容变更时间,最好能对得上。三者不一致时,蜘蛛会倾向于相信反复验证过的那个信号。如果 Sitemap 说昨天更新、服务器的条件请求却说没变,这种矛盾积累多了,更新信号的可信度就会下降。

另外要留意:304 只解决“同一个 URL 重复取”的成本问题。如果同一份内容有多个入口 URL,蜘蛛仍然会分别去取,每个 URL 都要各自走一次条件请求。重复入口的收敛,还是得回到规范标签和内链整理上。

一个简单的自查清单

  • 随机挑几个页面,连续请求两次,看第二次是否返回 304。
  • 改动页面内容后,确认 ETag 和 Last-Modified 确实变了。
  • 检查日志里 304 与 200 的比例,如果几乎全是 200,说明缓存协商没生效。
  • 确认 304 响应里没有夹带正文。
  • 核对 Sitemap 的 lastmod 与服务器返回的时间头是否一致。

缓存协商不决定蜘蛛会不会来,它决定的是每次来的代价。把 304 用对,服务器压力小一点,蜘蛛在同样的配额里就能走得更远一点。