蜘蛛抓一個頁面,往往不是一次性的。内容没變,它過几天還會再来一趟;變化频繁的頁面,回来的間隔更短。對服務器来说,這些回訪里绝大多數請求拿不到任何新信息,却要重新渲染一次頁面。缓存协商就是為這種场景准备的:让服務器在内容没變时明确告诉蜘蛛“没變”,而不是把整份 HTML 再吐一遍。
條件請求是怎么發生的
蜘蛛手里存着上次抓取的结果,回訪时會带上两個头里的一個或两個:If-Modified-Since(配合上次响應里的 Last-Modified)和 If-None-Match(配合 ETag)。服務器比對之後,如果内容没變,返回 304 Not Modified,不带正文;變了就正常返回 200 和新内容。
這個過程對蜘蛛是透明的,它發的是一個普通 GET,只是多带了條件头。服務器返回 304,蜘蛛就知道本地副本還能繼續用。
304 省的是什么,不省的是什么
- 省的是带宽和服務器渲染開销。大頁面、需要查库拼装的頁面,304 能省掉相当一部分成本。
- 省的是蜘蛛處理頁面的時間。没有正文要解析,這一轮的消耗更小。
- 不省的是抓取次數。一次 304 依然是一次抓取請求,依然占用抓取配額。想让蜘蛛少来几趟,靠的是内容更新节奏和站点整体质量,不是 304。
304 是“這次別传了”,不是“以後別来了”。
什么时候返回 304 反而有風險
最常见的错誤,是把 304 当成萬能的省流開關:
- 内容已经變了却返回 304。常见原因是 ETag 或 Last-Modified 没跟着内容更新,或者 CDN 缓存住了舊版本。蜘蛛會繼續用舊快照,用戶看到的却是新頁面。
- ETag 每次請求都變。有些服務器用進程 ID、時間戳甚至随机值生成 ETag,同一個 URL 每次返回都不一样,條件請求永遠匹配不上,等于白设。
- 把 304 当屏蔽手段。想让蜘蛛別抓某個頁面,應该用 robots.txt 或 404/410,而不是對所有請求一律回 304。長期返回 304 而内容又對不上,只會让服務器和蜘蛛的信号互相打架。
配置上的几個稳妥做法
- 静態资源:给足缓存時間,配合 ETag 或 Last-Modified,让回訪直接命中 304。
- 動態頁面:如果内容變化由資料驱動,把 Last-Modified 绑定到真實的資料更新時間,而不是頁面生成時間。
- ETag 尽量基于内容哈希,保證同一份内容在不同机器、不同進程下算出同样的值。
- 304 响應不要带正文,也不要在這類响應里改變头部语义,保持干净。
- CDN 與源站的缓存头保持一致,避免邊缘节点和源站各自為政,一會儿 200 一會儿 304。
和 Sitemap、内鏈放在一起看
Sitemap 里的 lastmod、頁面自身的 Last-Modified、實际内容變更時間,最好能對得上。三者不一致时,蜘蛛會倾向于相信反复驗證過的那個信号。如果 Sitemap 说昨天更新、服務器的條件請求却说没變,這種矛盾积累多了,更新信号的可信度就會下降。
另外要留意:304 只解决“同一個 URL 重复取”的成本問题。如果同一份内容有多個入口 URL,蜘蛛仍然會分別去取,每個 URL 都要各自走一次條件請求。重复入口的收敛,還是得回到規范标簽和内鏈整理上。
一個简單的自查清單
- 随机挑几個頁面,连續請求两次,看第二次是否返回 304。
- 改動頁面内容後,確認 ETag 和 Last-Modified 确實變了。
- 检查日誌里 304 與 200 的比例,如果几乎全是 200,說明缓存协商没生效。
- 確認 304 响應里没有夹带正文。
- 核對 Sitemap 的 lastmod 與服務器返回的時間头是否一致。
缓存协商不决定蜘蛛會不會来,它决定的是每次来的代價。把 304 用對,服務器压力小一点,蜘蛛在同样的配額里就能走得更遠一点。