搜尋抓取

304 與缓存头:蜘蛛再次来訪时,服務器该回什么

蜘蛛對同一個 URL 的重复訪問遠多于首次發現,服務器返回 200 還是 304、缓存头怎么配,會直接影响抓取节奏與带宽開销。本文拆解 ETag、Last-Modified 與條件請求的工作方式,列出集群环境下常见的配置失誤,並给出运营侧的核查清單。

搜尋抓取

304 與缓存头:蜘蛛再次来訪时,服務器该回什么

很多人把注意力放在“新 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 對不同来源返回不同缓存策略,蜘蛛拿到的可能恰好是最不友好的那一版。

可以立刻做的几項核查

  1. 用 curl -I 连續請求同一個 URL 两次,看第二次是否返回 304,以及 ETag 是否一致。
  2. 把請求分別打到不同的後端节点上,比對 ETag 和 Last-Modified 是否稳定。
  3. 找一個刚改過的頁面,確認它的 Last-Modified 确實變了,而不是留在舊時間上。
  4. 检查 CDN 與源站的缓存規則,確認 HTML 的缓存时長符合你的更新节奏。
  5. 留意响應時間。如果條件請求的响應比完整請求還慢,說明比對逻辑本身開销過大,值得優化。

這些都不是能立刻带来排名變化的大動作,但它們决定了蜘蛛每次回来时是不是“白跑一趟”。把重复訪問的這部分成本压低,剩下的抓取机會才能留给真正需要更新的頁面。