蜘蛛每天都会来,但你的页面大部分时间并没有变化。如果每次访问都完整返回一遍 HTML,带宽、服务器处理时间和抓取时间就花在了重复内容上。HTTP 的条件请求机制可以把这部分省下来:服务器只回一句“没变”,蜘蛛就不用再下载正文。
条件请求是怎么工作的
蜘蛛第一次抓取某个 URL 时,服务器可以在响应头里给出两个标识之一:ETag(内容的指纹)或 Last-Modified(最后修改时间)。下次再来,蜘蛛会带上 If-None-Match 或 If-Modified-Since,把上次拿到的值发回来。服务器比对后如果认为内容没变,就返回 304 Not Modified,响应里不带正文。
- 200:内容有变化,或服务器无法判断,返回完整页面。
- 304:内容没变,蜘蛛沿用本地已有的版本。
它对抓取的实际影响
304 不会让蜘蛛“少来”,它仍然会访问这个 URL,仍然占用一次请求。省下的是这次请求的传输量,以及服务器重新渲染、序列化页面的成本。当站点 URL 数量大、更新频率低时,这个差别会累积:服务器响应更轻,蜘蛛在同样的抓取时间预算里走得更顺。
反过来说,如果每个页面每次都要重新生成、重新传一遍完整 HTML,响应时间和带宽都压在服务器上,抓取高峰期更容易出现超时和 5xx。
几个容易踩的坑
- ETag 每次都变。有些框架用进程 ID、时间戳或文件 inode 生成 ETag,同一个文件两次请求得到两个不同值。蜘蛛每次都判定内容变了,永远拿 200,条件请求形同虚设,还多了一次无效比对。
- Last-Modified 永远是当前时间。模板里直接输出 now(),蜘蛛看到“刚刚修改过”,于是反复重抓。
- 页面里有易变内容。页脚时间、随机推荐位、访问计数器,会让内容指纹每次都不同。这类元素要么改为异步加载,要么在服务器端缓存固定。
- CDN 或反向代理把头剥掉。源站给了 ETag,边缘节点没有透传,蜘蛛拿到的响应里就没有可用标识。
- 错误的 304。内容确实更新了却返回 304,蜘蛛会继续用旧版本,新内容迟迟进不了索引。这类问题排查起来很费时间。
怎么检查和配置
- 用 curl 连续请求同一个 URL 两次,第二次带上第一次返回的 ETag 或 Last-Modified,看是否返回 304。
- 翻服务器日志,统计全部请求与蜘蛛请求里 304 的占比。占比长期接近零,说明条件请求基本没生效。
- 确认源站配置正确后,再检查 CDN 是否透传 ETag 和 Last-Modified。
- 对确实频繁变动的页面,不要硬造 304,保持 200 更安全。
条件请求省的是重复传输,不是抓取次数。把它当成服务器侧的减负手段,而不是用来控制蜘蛛来不来的开关。
和 Sitemap 里的 lastmod 不是一回事
Sitemap 中的 lastmod 是你在清单里声明的时间,属于主动汇报;响应头里的 ETag 与 Last-Modified 是每次请求时的现场比对,属于实时确认。两者可以配合,但不能互相替代:lastmod 写得不准,最多是判断参考失真;304 判断错了,蜘蛛可能长期停留在旧版本上。
小结一下:把 ETag 和 Last-Modified 配对好,让没变的页面老实返回 304,让变了的页面老老实实返回 200,是抓取效率里成本很低、收益却比较稳的一环。