搜索抓取

304 响应与缓存头:让蜘蛛少重复下载没变的页面

蜘蛛每次来访都要重新取一遍 HTML,如果页面内容没变,这次下载就是白花成本。本文讲 Last-Modified、ETag 两种协商方式怎么配合蜘蛛的请求头,什么情况下服务器该回 304,以及常见的缓存头失效写法,帮站点把抓取资源留给真正更新的页面。

搜索抓取

304 响应与缓存头:让蜘蛛少重复下载没变的页面

蜘蛛为什么反复下载同一个页面

蜘蛛来抓一个页面,无论内容是否变化,默认都要把 HTML 完整取一遍。对站点来说,带宽和响应时间是一部分成本;对蜘蛛来说,它的抓取容量有限,花在没变页面上的次数越多,能分给新页面和更新页面的就越少。缓存协商解决的就是这件事:让服务器有机会告诉蜘蛛「这一份和你上次拿的一样,不用再下了」。

两种协商方式:时间戳与内容指纹

Last-Modified 与 If-Modified-Since

服务器在响应头里给出资源的最后修改时间。蜘蛛下次抓取时,会带上 If-Modified-Since,值就是它上次记下的那个时间。服务器比较后,如果之后没改过,就回 304;改过就返回完整内容和新时间。这一套依赖服务器给出的时间准确。

ETag 与 If-None-Match

ETag 是资源的一个标识串,由服务器按内容或版本生成。蜘蛛下次请求时带 If-None-Match,值就是上次拿到的 ETag。标识一致就回 304。相比时间戳,ETag 的好处是不受时钟和格式精度影响,但前提是这个串要稳定——同一个内容,每次算出来必须一样。

返回 304 时,蜘蛛拿到的是什么

304 不等于「页面被删」,也不等于「不要抓」。它只表示这次没有新内容可给,蜘蛛会沿用本地已有的副本做后续判断。真正影响是否重新处理的是内容本身有没有变,而不是响应码是 200 还是 304。所以站点不必担心大量 304 会让页面掉出索引,需要担心的是反过来——该回 304 的时候回了完整内容,白白增加双方负担。

常见的几种缓存头失效写法

  • 把 Last-Modified 设成「当前时间」,每次请求都变,协商永远命中不了。
  • ETag 由随机数、进程号或请求时间拼成,同一份内容每次标识都不同。
  • 反向代理或 CDN 在回源时丢弃、改写这两个头,源站设了也不生效。
  • 页面直接输出 no-store 或 no-cache,等于告诉中间层每次都当新的处理。
  • 静态文件靠查询串刷版本号,内容其实没改,但 URL 变了,缓存也就重新来过。

和 Sitemap 里的 lastmod 不要互相矛盾

两者说的是不同层的事:lastmod 是站点主动声明「这一页什么时候变过」,缓存头是服务器在单次请求里做的协商。如果 Sitemap 里写本周更新、响应头里的修改时间却是半年前,蜘蛛会收到两个不一致的信号。更稳妥的做法是让它们来自同一个数据源,页面内容真正落库的时间,既写进 Sitemap,也用于生成 Last-Modified。

自查顺序

  1. 用命令行工具带 -I 请求目标页,看响应头里有没有 Last-Modified 和 ETag。
  2. 手动带上 If-Modified-Since 或 If-None-Match 再请求一次,确认真的返回 304,而不是仍然 200。
  3. 翻站点日志,统计一段时间内 304 的比例。更新不频繁的栏目页、文章页,比例本就不该太低。
  4. 如果用了 CDN,检查回源配置是否保留了这两个头,以及缓存策略有没有覆盖源站设置。

注意边界

缓存协商是省事的手段,不是省内容的借口。更新频繁的首页、列表页,该改就如实改,修改时间要跟着内容走;不要为了少被下载而把时间人为压住。另外,只靠缓存头并不能替代抓取入口和链接结构的建设,它解决的只是「重复下载」这一环。对站点来说,合理的做法是:每个环节各司其职,让蜘蛛把次数花在真正有变化的地方。

一个简单的判断标准:如果一个页面三个月没动过,蜘蛛每次来还完整下载一遍,那就该看看缓存头了。