蜘蛛回爬一个 URL 时,并不是每次都把整页重新下载一遍。如果服务器支持条件请求(Conditional Request),它可能只拿到一个 304 响应,然后继续使用本地已有的版本。这个机制表面上是省流量,实际上决定了蜘蛛能不能准确判断“这个页面到底变没变”。
条件请求在抓取里是怎么跑的
第一次抓取时,服务器在响应头里给出 Last-Modified 或 ETag。蜘蛛把这两个值和页面内容一起存下来。下次再来的时候,它会在请求头里带上 If-Modified-Since(对应 Last-Modified)或 If-None-Match(对应 ETag):
- 服务器判断内容没变,返回 304,响应体是空的;
- 服务器判断内容变了,返回 200 和完整页面;
- 服务器不支持这两个头,就只能每次都返回 200。
对蜘蛛来说,304 和 200 都算一次有效抓取,URL 是被访问到的。区别在于它有没有更新索引里存的那份内容。
Last-Modified 最容易出问题的地方
很多站点的时间戳是程序动态生成的,常见毛病有几种:
- 每次请求都返回当前时间。蜘蛛每次都以为内容变了,把同一段 HTML 反复当作新版本处理,抓取收益很低,还可能让它觉得这个站点的变更信号不可信。
- 时间格式不合法。Last-Modified 应该是 HTTP-date 格式,带 GMT 时区,写成“2024-05-01 10:00”这种本地格式,部分客户端解析不出来就直接忽略。
- 时区处理错误。服务器本地时间没转成 GMT,会让时间出现几小时偏差,配合容忍度之后可能变成“内容好像一直没变”。
- 内容更新了,时间没动。比如模板改了、字段改了,但文件 mtime 还是旧的,蜘蛛自然拿不到新版本。
ETag 要小心多机不一致
常见的 ETag 生成方式有两种:一种根据文件大小和修改时间算,一种根据内容哈希算。前者在多台服务器、多个容器实例的环境下很容易出问题——同一个 URL 落到不同机器上,算出来的 ETag 不一样,蜘蛛每次拿到的标识都对不上,只能重新下载整页。
相比之下,基于内容哈希的强 ETag 更稳定,计算成本稍高。如果站点前面还有 CDN,还要确认 CDN 是原样透传 ETag,还是自己重写了一个:两边不一致,同样会让判断失效。
304 变多,会不会影响抓取频率
短期内不会。304 说明页面可达、服务器响应正常,这本身是好信号。但如果一个 URL 连续多次回爬都是 304,蜘蛛通常会逐步降低它的访问频率,把抓取配额让给看起来更活跃的 URL。反过来,如果一个页面内容经常变却总是返回 304,蜘蛛就会长期停留在旧版本上,索引内容和实际内容脱节。
所以真正要关心的不是 304 的数量,而是 304 是否和实际更新情况对得上。
和 Sitemap、内链的配合
Sitemap 里的 lastmod 和响应头里的 Last-Modified 最好保持一致。如果 Sitemap 天天写今天的日期,响应头却永远返回三年前的 Last-Modified,两个信号互相打架,蜘蛛会倾向于相信其中一个,而那个往往不是你希望它相信的。
内链层面则要注意:如果一个更新频繁的列表页指向的详情页长期 304,蜘蛛在这条路径上就走得很少;而结构上被多次引用的 URL,更容易被重新安排抓取,从而拿到新的 200。
怎么自查
- 用 curl -I 或浏览器开发者工具,连续请求同一个 URL 两次,看返回头和状态码;
- 确认 Last-Modified 是否为 GMT 格式,ETag 是否每次请求都变化;
- 如果站点在多台机器或容器上,逐个实例请求一遍,对比 ETag 是否一致;
- 检查 CDN 回源与缓存策略,确认它没有把响应头改掉;
- 在抓取日志里看 304 的占比,以及哪些 URL 长期只出现 304。
条件请求的意义,是让蜘蛛用更少的成本确认内容状态。配置对了是效率,配置错了就是误导——它以为自己看到的是最新的,其实一直是旧的。