条件请求在抓取流程里做了什么
搜索蜘蛛并不是每次访问都从头下载页面。对于已经抓过、并且记下过校验信息的 URL,下一次请求会带上 If-Modified-Since(对应 Last-Modified)或 If-None-Match(对应 ETag)。服务器判断内容没变,就返回 304 Not Modified,不带响应体。对站点来说省掉了正文传输,对抓取方来说这次访问的代价也更低。
需要说清楚的是,304 不会让某个 URL 变成“已更新”,它只是一次确认。所以它不能替代内容更新,也不该被当成提高抓取频率的手段。它的价值在于:页面确实没变的时候,别让服务器白白吐一遍完整 HTML。
Last-Modified 与 ETag 的写法要点
Last-Modified
它应该是该 URL 内容最后一次实质变化的 GMT 时间,格式形如 Wed, 21 Oct 2025 07:28:00 GMT。有几点要留意:时间只能向前,不能每次请求都返回当前时间;时区必须是 GMT;格式要标准,缺项或用了本地时间字符串会让解析失败,条件请求直接失效。
ETag
ETag 是内容指纹,常见做法是对正文做哈希。强校验写成带引号的形式,弱校验加 W/ 前缀。如果页面由模板拼装,注意别把随机数、时间戳、会话 ID 混进哈希源,否则每次请求 ETag 都不同,等于没配。
两个头同时存在时,服务器应按规范做优先级判断,蜘蛛也会一并携带。只配一个够用,配齐更稳。
常见的配置坑
- Last-Modified 用了应用启动时间或当前时间,每次请求都在变,页面等于天天“刚更新”。
- 缓存策略写成 no-store 或 no-cache,中间层把条件请求头丢掉,回源拿到的永远是 200。
- CDN 和源站各算一套 ETag,边缘返回一个、回源返回另一个,蜘蛛手里的校验值一直对不上。
- Vary 头设置过宽,同一条 URL 被拆成大量缓存变体,304 命中率随之下降。
- 对登录态、个性化区块不做区分,同一个 URL 对不同请求返回不同内容,校验值天然不稳定。
这些坑有个共同特征:从抓取日志看就是 200 一大堆,几乎见不到 304。可以把它当成一个排查信号。
从日志核对 304 的比例
抓取日志里记录了状态码。把同一时间段内某个目录或某个模板下的请求拉出来,统计 200 与 304 的比例。内容长期稳定的栏目页、详情页,如果几乎全是 200,就值得回头检查缓存头和校验头的配置。反过来,更新频繁的列表页全是 304 也不正常,说明校验值没有被正确更新。
核对时最好按模板分组,而不是全站算一个总比例。新闻列表页和帮助文档页的合理值显然不同。
与 Sitemap 里 lastmod 的关系
这两个时间解决的不是同一件事。304 是服务器对单次请求的实时回答,lastmod 是站点主动告诉抓取方这条 URL 什么时候变过。如果 lastmod 天天变而内容实际没动,抓取方来了一看是 304,长期下来这个字段的可信度就会下降。两者应当保持一致:内容真变了,lastmod 更新、ETag 变化、返回 200;内容没变,lastmod 不动、返回 304。
上线前可以走的检查清单
- 用一个固定 URL 连续请求两次,第二次带上 If-None-Match 或 If-Modified-Since,确认返回 304。
- 观察 Last-Modified 在多次请求间是否稳定,只在内容改动后才变化。
- 确认 CDN 没有剥离条件请求头,回源行为与直连源站一致。
- 检查是否误用了 no-store,导致中间层完全不参与缓存协商。
- 按模板抽样统计日志中的 304 占比,找出明显异常的分组。
304 只是一个减负手段。该更新的内容、该修的内链、该补的入口,还是要老老实实做,别指望靠一个响应码换来更好的抓取表现。