很多人把注意力放在“新 URL 怎么被蜘蛛发现”上,却忽略了一个更常见的场景:同一个 URL 被反复访问。对大多数站点来说,蜘蛛每天的请求里,重复抓取的占比远高于首次发现。这些重复请求里,服务器每一句回应,都在影响下一次抓取的节奏和开销。
第二次来访和第一次,区别在请求头
蜘蛛第一次抓某个页面时,拿到的是一份完整响应:状态码 200,连同正文一起。它同时会记下响应里带的缓存标识,比如 ETag 和 Last-Modified。
下一次它再来,就不是空手来了。请求头里会带上 If-None-Match(对应你上次给的 ETag)或 If-Modified-Since(对应 Last-Modified)。这就是条件请求。服务器拿着这两个值去比对:内容没变,回一个 304 和空正文;内容变了,回 200 和新的正文。
所以服务器回的其实不是一句“在不在”,而是“变没变”。
ETag 在多台机器上容易失准
ETag 的常见生成方式有两种:一种是内容哈希,另一种是文件修改时间加长度拼出来的字符串。问题往往出在负载均衡后面有多台机器的时候。
如果每台机器的 ETag 算法带上了机器标识或随机因子,同一个页面轮流落到不同机器上,蜘蛛拿到的 ETag 就会来回变。它会认为内容一直在变,于是每次都发完整请求,每次都收到 200。这不仅浪费带宽,也让“这个页面到底更新了没有”这个信号变得不可信。
解决方向很直接:让同一份内容在所有节点上算出同一个 ETag,或者干脆只依赖 Last-Modified。这属于运维层面的统一,值得让技术同学确认一次。
Last-Modified 别写成“当前时间”
为了让响应看起来“新鲜”,有些程序会在每次请求时把 Last-Modified 设成此刻。这等于告诉蜘蛛页面每秒都在变。
合理的做法是跟内容本身绑定:文章正文改了就更新,模板换了、广告位调整了、页脚改版权年份,这些都不该动这个时间。如果分不清,宁可让它偏保守,也不要让它频繁抖动。
抓取节奏不是靠多报“我更新了”争取来的。频繁误报变化,最后损失的是这个字段本身的参考价值。
304 省下的是什么,没省下的是什么
304 省下的是正文传输和服务器拼装页面的开销,对带宽和响应时间都有好处。但它并不等于这次访问没发生过——蜘蛛仍然发起了请求,仍然占用了一次抓取机会。
所以,别把 304 当成“减少抓取”的手段。真正决定抓取频次的,还是内容更新的实际频率、站点的整体质量表现,以及服务器响应是否稳定。缓存策略只是让同样次数的抓取更轻一点。
几种常见的缓存头配置问题
- 给 HTML 配了很长的 max-age。某些 CDN 默认策略会把 HTML 也缓存很久,结果蜘蛛拿到的是过期版本,页面明明改了,它看到的是旧内容。
- 全站 no-store。有些安全策略会把所有响应设成不缓存。蜘蛛依然能抓,只是每次都得拿完整正文,重复访问的成本更高。
- ETag 和 Last-Modified 一起给,但互相对不上。两个标识指向不同的更新时间,比对逻辑会变得混乱。
- 动态页面的 Last-Modified 永远等于请求时间。前面说过的老问题,在列表页和搜索页上尤其常见。
- 缓存头跟着登录态走。同一 URL 对不同来源返回不同缓存策略,蜘蛛拿到的可能恰好是最不友好的那一版。
可以立刻做的几项核查
- 用 curl -I 连续请求同一个 URL 两次,看第二次是否返回 304,以及 ETag 是否一致。
- 把请求分别打到不同的后端节点上,比对 ETag 和 Last-Modified 是否稳定。
- 找一个刚改过的页面,确认它的 Last-Modified 确实变了,而不是留在旧时间上。
- 检查 CDN 与源站的缓存规则,确认 HTML 的缓存时长符合你的更新节奏。
- 留意响应时间。如果条件请求的响应比完整请求还慢,说明比对逻辑本身开销过大,值得优化。
这些都不是能立刻带来排名变化的大动作,但它们决定了蜘蛛每次回来时是不是“白跑一趟”。把重复访问的这部分成本压低,剩下的抓取机会才能留给真正需要更新的页面。