蜘蛛對一批 URL 的回訪是持續的。第一次抓取拿到完整响應体,之後的每一次,如果服務器仍然把整頁重發一遍,带宽、資料库查询和抓取资源都在做重复功。HTTP 的缓存协商机制正是為這種场景准备的:蜘蛛带着上一次拿到的标记来問“内容變了吗”,服務器可以只回一個 304 Not Modified,不带正文。
條件請求長什么样
蜘蛛第二次訪問时,通常會在請求头里带上两個字段中的一個或两個:
- If-None-Match:值為上一次响應头里返回的 ETag,相当于内容的指纹。
- If-Modified-Since:值為上一次响應头里返回的 Last-Modified 時間。
服務器比對之後,要么返回 200 加完整正文,要么返回 304 說明副本還能用。從抓取角度看,後者的成本明顯更低。
304 能省下什么
- 响應体的传輸量。一個頁面 100KB,一天被回訪十次,一個月就是几十 GB 的差別。
- 服務端渲染與查询的開销。動態站点的压力往往体現在這里。
- 抓取资源的占用。不同搜尋引擎對 304 的計費方式不完全相同,但通常都比 200 轻。
需要说清的是,304 只說明“這份内容没變”,它並不等于蜘蛛會因此提高抓取频次,也不等于頁面會被收錄。它是一項成本優化,不是一個可以被拿来“刷”的開關。
什么时候不该回 304
配置不当的时候,304 反而會让蜘蛛長期拿着過期副本。以下场景需要重点排查:
- 内容變了但 ETag 没變。例如 ETag 寫成了固定字符串、模板版本号或站点统一 ID,那么無论正文怎么改,比對都會命中。
- 多台机器各算各的 ETag。集群里没有把 ETag 生成規則统一,蜘蛛在 A 机拿到指纹、去 B 机比對,结果永遠是 200,等于白做。
- 時間字段被截断或时区混乱。Last-Modified 精确到秒,If-Modified-Since 却按天比對,或者服務器时区與生成時間不一致,都會让判断错位。
- 頁面里带實时時間戳。Last-Modified 每次都變,蜘蛛每次都拿全量,协商机制形同虚设。
和 Sitemap 里 lastmod 的關系
很多人把這两件事分開看,其實它們是一组声明與確認:
- Sitemap 的 lastmod 是你主動声明“這個 URL 什么时候更新過”。
- 响應头的 Last-Modified 與 304 是服務器被動確認這個声明。
如果 lastmod 每天在變,服務器却始终回 304,声明就失去了可信度;反過来,正文确實改了,ETag 和 Last-Modified 也要跟着更新,否則蜘蛛會一直沿用舊副本。两邊保持一致,比單方面把時間戳改新更有意义。
一份可以照着做的检查清單
- 用命令行带條件头發起一次請求,確認返回碼符合预期:curl -I -H "If-None-Match: <上次的 ETag>" 頁面地址。
- 改一次正文,重新請求,確認返回的是 200 而不是 304。
- 在多台後端机器上分別請求,對比同一 URL 的 ETag 是否一致。
- 在服務器日誌里統計 304 的占比。占比過低說明协商基本没生效,過高則要抽查内容更新後有没有及时變化。
- 頁面下线或改版时,確認舊 URL 的缓存标记不會繼續让蜘蛛拿到废弃内容。
304 是用来省成本的,不是用来掩盖變更的。寫死一個永遠不變的 ETag,看似让蜘蛛“少拿点内容”,實际上是让它一直拿着一份過期副本在跑。
把條件請求配好,收益不會立刻体現在抓取量上,但會在服務器负载、日誌里的重复传輸和内容更新的及时性上慢慢顯現。對于頁面數量多、更新频繁的站点,這部分優化的性價比通常高于再去找新的抓取入口。