蜘蛛回爬一個 URL 时,並不是每次都把整頁重新下载一遍。如果服務器支持條件請求(Conditional Request),它可能只拿到一個 304 响應,然後繼續使用本地已有的版本。這個机制表面上是省流量,實际上决定了蜘蛛能不能准确判断“這個頁面到底變没變”。
條件請求在抓取里是怎么跑的
第一次抓取时,服務器在响應头里给出 Last-Modified 或 ETag。蜘蛛把這两個值和頁面内容一起存下来。下次再来的时候,它會在請求头里带上 If-Modified-Since(對應 Last-Modified)或 If-None-Match(對應 ETag):
- 服務器判断内容没變,返回 304,响應体是空的;
- 服務器判断内容變了,返回 200 和完整頁面;
- 服務器不支持這两個头,就只能每次都返回 200。
對蜘蛛来说,304 和 200 都算一次有效抓取,URL 是被訪問到的。区別在于它有没有更新索引里存的那份内容。
Last-Modified 最容易出問题的地方
很多站点的時間戳是程序動態生成的,常见毛病有几種:
- 每次請求都返回目前時間。蜘蛛每次都以為内容變了,把同一段 HTML 反复当作新版本處理,抓取收益很低,還可能让它觉得這個站点的變更信号不可信。
- 時間格式不合法。Last-Modified 應该是 HTTP-date 格式,带 GMT 时区,寫成“2024-05-01 10:00”這種本地格式,部分客戶端解析不出来就直接忽略。
- 时区處理错誤。服務器本地時間没轉成 GMT,會让時間出現几小时偏差,配合容忍度之後可能變成“内容好像一直没變”。
- 内容更新了,時間没動。比如模板改了、字段改了,但文件 mtime 還是舊的,蜘蛛自然拿不到新版本。
ETag 要小心多机不一致
常见的 ETag 生成方式有两種:一種根據文件大小和修改時間算,一種根據内容哈希算。前者在多台服務器、多個容器實例的环境下很容易出問题——同一個 URL 落到不同机器上,算出来的 ETag 不一样,蜘蛛每次拿到的标识都對不上,只能重新下载整頁。
相比之下,基于内容哈希的强 ETag 更稳定,計算成本稍高。如果站点前面還有 CDN,還要確認 CDN 是原样透传 ETag,還是自己重寫了一個:两邊不一致,同样會让判断失效。
304 變多,會不會影响抓取频率
短期内不會。304 說明頁面可達、服務器响應正常,這本身是好信号。但如果一個 URL 连續多次回爬都是 304,蜘蛛通常會逐步降低它的訪問频率,把抓取配額让给看起来更活跃的 URL。反過来,如果一個頁面内容经常變却總是返回 304,蜘蛛就會長期停留在舊版本上,索引内容和實际内容脱节。
所以真正要關心的不是 304 的數量,而是 304 是否和實际更新情况對得上。
和 Sitemap、内鏈的配合
Sitemap 里的 lastmod 和响應头里的 Last-Modified 最好保持一致。如果 Sitemap 天天寫今天的日期,响應头却永遠返回三年前的 Last-Modified,两個信号互相打架,蜘蛛會倾向于相信其中一個,而那個往往不是你希望它相信的。
内鏈层面則要注意:如果一個更新频繁的列表頁指向的詳情頁長期 304,蜘蛛在這條路径上就走得很少;而结构上被多次引用的 URL,更容易被重新安排抓取,從而拿到新的 200。
怎么自查
- 用 curl -I 或浏览器開發者工具,连續請求同一個 URL 两次,看返回头和狀態碼;
- 確認 Last-Modified 是否為 GMT 格式,ETag 是否每次請求都變化;
- 如果站点在多台机器或容器上,逐個實例請求一遍,對比 ETag 是否一致;
- 检查 CDN 回源與缓存策略,確認它没有把响應头改掉;
- 在抓取日誌里看 304 的占比,以及哪些 URL 長期只出現 304。
條件請求的意义,是让蜘蛛用更少的成本確認内容狀態。配置對了是效率,配置错了就是誤導——它以為自己看到的是最新的,其實一直是舊的。