站点运营

站点运营:304 與缓存头,蜘蛛再来时该给什么响應

蜘蛛第二次訪問同一個頁面,服務器该返回 200 還是 304?本文從條件請求说起,讲清 Last-Modified 與 ETag 的配合方式、Cache-Control 的常见寫法,以及多机部署和 CDN 场景下缓存头容易踩的坑,並给出可操作的驗證步骤。

站点运营

站点运营:304 與缓存头,蜘蛛再来时该给什么响應

蜘蛛抓取一個頁面,並不總是把整份 HTML 重新下载一遍。如果站点支持條件請求,服務器就有机會只回一個 304,双方都省事:蜘蛛少下载一份重复内容,服務器少传一次响應体。這個机制的開關,就藏在响應头里。

條件請求:蜘蛛會带着凭證来

当爬虫抓到過某個 URL 之後,再次訪問时通常會在請求头里带上 If-Modified-Since(時間)或者 If-None-Match(ETag 值)。服務器拿到這两個值,與目前资源版本比對:没變就返回 304 Not Modified,响應体為空;變了就返回 200 和新内容。

很多人只關注 200 和 404,忽略了 304。其實從日誌里看 304 的占比,能判断站点是否在做有效的版本管理。如果一個頁面几個月没改,蜘蛛每次来都拿到 200 全量传輸,這部分带宽和响應時間多少有些浪費。

Last-Modified 與 ETag 別互相打架

Last-Modified 依赖文件的修改時間,對静態文件很自然;ETag 是服務端算出的版本标识,可以是内容哈希,也可以是別的規則。两者可以同时存在,但要注意一致性。

  • 内容没變,Last-Modified 却每次請求都在變,比如動態寫入的目前時間戳,蜘蛛就會認為頁面一直在更新。
  • ETag 用了不稳定的值,比如進程 ID、随机數,條件請求會失效,全部退化成 200。
  • 两個头都给的时候,一般優先判断 ETag;两者结论冲突时行為容易混乱。要么只留一個,要么保證它們同進同退。

動態頁面的常见坑

列表頁、搜尋结果頁、带時間戳的模板,很容易無意間把目前時間寫進 Last-Modified。看上去没什么,實际效果是蜘蛛每次来都拿到 200,還可能因為誤判内容频繁更新而提高抓取频率,反而占用了別處的预算。要么干脆不做协商缓存,要么用真實的最後編輯時間。

Cache-Control 是给谁看的

Cache-Control 主要约束浏览器和中間缓存,但對蜘蛛也有間接影响。常见几種寫法:

  • max-age 較長,配合内容指纹:适合图片、CSS、JS 這類不會原地修改的资源。
  • HTML 文档一般给較短的 max-age,或者用 no-cache,即可以缓存但每次要回源校驗,保證改版後較快生效。
  • no-store 是完全不缓存,用在下發敏感信息的頁面上没問题,套在普通内容頁就有点浪費。

需要留意的是,no-cache 並不等于不缓存,它只是要求每次向服務器確認;真正禁止存储的是 no-store。這两個词寫混,是排查缓存問题时最常见的誤會。

CDN 這一层要單獨確認

開啟 CDN 之後,响應头可能来自邊缘节点而不是源站。這里容易出現几類問题:

  • 把 301、404 甚至 500 的响應也缓存了很久,源站修好了,蜘蛛拿到的還是舊的错誤狀態。
  • 缓存了带 Set-Cookie 的頁面,或者把登入態頁面缓存成了公共版本。
  • 各节点缓存版本不一致,同一 URL 在不同地区返回内容不同,蜘蛛拿到的版本随机。

建议對 HTML 文档的缓存規則保守一些,错誤狀態碼不缓存或者只缓存很短時間,静態资源再放開。

怎么检查和驗證

  1. 用 curl -I 或浏览器網絡面板查看响應头,確認是否有 ETag、Last-Modified、Cache-Control。
  2. 连續請求两次,看第二次是否返回 304;如果始终是 200,检查版本标识是否稳定。
  3. 在服務器日誌里統計 304 與 200 的比例,结合响應時間看有没有實际收益。
  4. 修改一次正文内容,確認版本标识同步變化,蜘蛛下次来訪能拿到新内容。
缓存和 304 的目标是少传重复資料,而不是把更新藏起来。省流量的前提是:内容變了,蜘蛛一定看得出来。