蜘蛛把一个 URL 抓走之后,事情并没有结束。过一段时间它会回来复查,确认页面有没有变化。这个复查动作在抓取日志里占比很高,如果每次回来都要把整份 HTML 重新传一遍,消耗的是双方的带宽,也占用了本该留给新 URL 的抓取时间。条件请求与 304 响应,就是为这件事准备的。
一、蜘蛛复查时,其实是在做条件请求
不少搜索引擎蜘蛛在重访已知 URL 时,会带上 If-Modified-Since 或 If-None-Match 请求头。前者的值来自上一次响应里的 Last-Modified,后者来自上一次的 ETag。这相当于在问服务器一句:从我上次拿到的那份文件之后,内容动过吗?
服务器端的回答通常有三种:
- 内容没变,返回 304 Not Modified,不带正文;
- 内容变了,返回 200,附带新的正文与新的 Last-Modified / ETag;
- 服务器不支持条件请求,直接返回 200 全量内容。
对抓取调度来说,304 是最省事的一种结果:蜘蛛确认页面还是老样子,转身去看别的 URL。长期来看,一个能稳定返回 304 的站点,在同样的抓取安排下可以覆盖更多页面。
二、Last-Modified 和 ETag 要经得起对比
Last-Modified 应当反映内容的真实修改时间
这个值来自文件的修改时间,或数据库中该条记录的更新时间,而不是每次请求的当前时间。页面内容没变,它就应该保持不变;反过来,只要正文有实质变化,就应该同步更新,否则蜘蛛会一直以为页面是旧的。
ETag 不要用随机值或时间戳生成
有些动态站点的 ETag 里混进了进程号、随机数或请求时间,导致同一个页面的 ETag 每次请求都不同。这样一来,蜘蛛每次都判断为内容有变化,条件请求形同虚设,返回的全是 200 全量正文。ETag 更适合由内容本身推导,比如对正文做一次哈希。
三、Sitemap 里的 lastmod 要和响应头说得一样
Sitemap 的 lastmod 是给蜘蛛看的第一手线索,响应头里的 Last-Modified 是第二次确认。两者如果互相矛盾,抓取判断就会变得混乱:Sitemap 写着今天更新,实际响应里 Last-Modified 还是三个月前,蜘蛛多跑一趟却什么都没拿到。
- 批量刷新的 lastmod(全站统一改成同一天)会让所有 URL 同时进入复查队列,短时间内的请求量明显上升;
- 只改模板、改侧栏而正文没动时,不一定需要更新 lastmod,除非这次改动确实影响页面主体内容;
- lastmod 一旦写成未来时间,部分蜘蛛会直接忽略这个字段。
四、几个容易踩的坑
- 用 304 掩盖更新。页面内容已经改了,服务器却因为缓存策略仍旧返回 304,蜘蛛会继续按旧版本处理。
- 内容没变却总返回 200 全量。浪费带宽,也让抓取时间花在重复内容上。
- 反向代理与源站的时间不一致。边缘节点缓存的 Last-Modified 与源站生成的对不上,条件请求屡屡失败,容易出现反复全量抓取。
- 把条件请求当成拒绝抓取的手段。304 的含义是内容未变,不是不欢迎访问。
五、一份可执行的检查清单
- 随机抽几个页面,用带 If-Modified-Since 的请求测一遍,看是否稳定返回 304。
- 对比同一页面连续两次响应的 ETag,确认它不会无故变化。
- 检查 Sitemap 里的 lastmod 与页面实际更新时间是否一致,剔除全站同一秒的批量值。
- 确认内容更新之后,Last-Modified 与 ETag 会同步变化。
- 在服务器日志里按状态码统计抓取比例,看 304 占到多少。
这些设置都不复杂,但需要站点在内容管理流程里把“更新时间”当成一个正经字段来维护。做对了,蜘蛛复查的代价变小,能用来发现和抓取新 URL 的余量就多一点。
304 不是省流量的技巧,而是告诉蜘蛛“这里没变化”的准确回答。回答得越准,抓取安排越顺。