蜘蛛抓取一個頁面,並不總是把整份 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 文档的缓存規則保守一些,错誤狀態碼不缓存或者只缓存很短時間,静態资源再放開。
怎么检查和驗證
- 用 curl -I 或浏览器網絡面板查看响應头,確認是否有 ETag、Last-Modified、Cache-Control。
- 连續請求两次,看第二次是否返回 304;如果始终是 200,检查版本标识是否稳定。
- 在服務器日誌里統計 304 與 200 的比例,结合响應時間看有没有實际收益。
- 修改一次正文内容,確認版本标识同步變化,蜘蛛下次来訪能拿到新内容。
缓存和 304 的目标是少传重复資料,而不是把更新藏起来。省流量的前提是:内容變了,蜘蛛一定看得出来。