蜘蛛对一批 URL 的回访是持续的。第一次抓取拿到完整响应体,之后的每一次,如果服务器仍然把整页重发一遍,带宽、数据库查询和抓取资源都在做重复功。HTTP 的缓存协商机制正是为这种场景准备的:蜘蛛带着上一次拿到的标记来问“内容变了吗”,服务器可以只回一个 304 Not Modified,不带正文。
条件请求长什么样
蜘蛛第二次访问时,通常会在请求头里带上两个字段中的一个或两个:
- If-None-Match:值为上一次响应头里返回的 ETag,相当于内容的指纹。
- If-Modified-Since:值为上一次响应头里返回的 Last-Modified 时间。
服务器比对之后,要么返回 200 加完整正文,要么返回 304 说明副本还能用。从抓取角度看,后者的成本明显更低。
304 能省下什么
- 响应体的传输量。一个页面 100KB,一天被回访十次,一个月就是几十 GB 的差别。
- 服务端渲染与查询的开销。动态站点的压力往往体现在这里。
- 抓取资源的占用。不同搜索引擎对 304 的计费方式不完全相同,但通常都比 200 轻。
需要说清的是,304 只说明“这份内容没变”,它并不等于蜘蛛会因此提高抓取频次,也不等于页面会被收录。它是一项成本优化,不是一个可以被拿来“刷”的开关。
什么时候不该回 304
配置不当的时候,304 反而会让蜘蛛长期拿着过期副本。以下场景需要重点排查:
- 内容变了但 ETag 没变。例如 ETag 写成了固定字符串、模板版本号或站点统一 ID,那么无论正文怎么改,比对都会命中。
- 多台机器各算各的 ETag。集群里没有把 ETag 生成规则统一,蜘蛛在 A 机拿到指纹、去 B 机比对,结果永远是 200,等于白做。
- 时间字段被截断或时区混乱。Last-Modified 精确到秒,If-Modified-Since 却按天比对,或者服务器时区与生成时间不一致,都会让判断错位。
- 页面里带实时时间戳。Last-Modified 每次都变,蜘蛛每次都拿全量,协商机制形同虚设。
和 Sitemap 里 lastmod 的关系
很多人把这两件事分开看,其实它们是一组声明与确认:
- Sitemap 的 lastmod 是你主动声明“这个 URL 什么时候更新过”。
- 响应头的 Last-Modified 与 304 是服务器被动确认这个声明。
如果 lastmod 每天在变,服务器却始终回 304,声明就失去了可信度;反过来,正文确实改了,ETag 和 Last-Modified 也要跟着更新,否则蜘蛛会一直沿用旧副本。两边保持一致,比单方面把时间戳改新更有意义。
一份可以照着做的检查清单
- 用命令行带条件头发起一次请求,确认返回码符合预期:curl -I -H "If-None-Match: <上次的 ETag>" 页面地址。
- 改一次正文,重新请求,确认返回的是 200 而不是 304。
- 在多台后端机器上分别请求,对比同一 URL 的 ETag 是否一致。
- 在服务器日志里统计 304 的占比。占比过低说明协商基本没生效,过高则要抽查内容更新后有没有及时变化。
- 页面下线或改版时,确认旧 URL 的缓存标记不会继续让蜘蛛拿到废弃内容。
304 是用来省成本的,不是用来掩盖变更的。写死一个永远不变的 ETag,看似让蜘蛛“少拿点内容”,实际上是让它一直拿着一份过期副本在跑。
把条件请求配好,收益不会立刻体现在抓取量上,但会在服务器负载、日志里的重复传输和内容更新的及时性上慢慢显现。对于页面数量多、更新频繁的站点,这部分优化的性价比通常高于再去找新的抓取入口。