搜尋蜘蛛回訪一個已经抓過的 URL 时,通常不會無條件地把整個 HTML 重新下载一遍。它會在請求头里带上條件:If-Modified-Since 或 If-None-Match。服務器如果判断内容没有變化,就返回 304 Not Modified,不带正文。這個机制對站点和蜘蛛双方都有意义,但它经常被誤解,也被错誤配置。
回訪請求里發生了什么
第一次抓取後,蜘蛛會记錄响應中的 Last-Modified 和 ETag。下次回訪时:
- If-Modified-Since 带上上次的 Last-Modified 時間;
- If-None-Match 带上上次的 ETag 值;
- 两者同时存在时,服務器按自身實現優先級判断。
如果内容未變,返回 304;如果有變化,返回 200 和新的正文。需要注意的是,304 並不等于這個 URL 不用再抓,它只是省掉了正文传輸。
304 省下的是什么
它主要减少三件事:带宽占用、服務器渲染或查询開销、以及响應時間。對于列表頁、标簽頁這類模板固定但内容偶尔變化的 URL,稳定的缓存协商能让回訪更快完成。但抓取预算的消耗並不能简單等同于字节數,蜘蛛仍要發起請求、等待响應、判断狀態碼。因此 304 不會让一個深层頁面凭空获得更多抓取机會。
把 304 当成降低服務器压力的手段是合理的,把它当成提高收錄概率的手段則會失望。
常见的缓存协商失效原因
- 動態 ETag:每次請求都生成不同的 ETag,比如把進程 ID、時間戳拼進去,導致永遠無法命中。
- Last-Modified 使用目前時間:頁面没變,時間却每次都刷新,蜘蛛只能收到 200。
- CDN 或反向代理改寫头:源站返回的 ETag 被邊缘节点替換,回訪时對不上。
- 压缩方式變化:同一 URL 在不同 Accept-Encoding 下产生不同 ETag,造成协商不一致。
- 會话或個性化内容:頁面里混入随机推荐、時間戳、用戶態片段,使内容實际每次都變。
核對步骤
- 用命令行工具带條件头發起請求,例如 curl 带上 If-None-Match 或 If-Modified-Since,观察是否返回 304。
- 连續两次無條件請求同一 URL,比較两次响應头中的 ETag 與 Last-Modified 是否一致。
- 查看源站直连與经過 CDN 後的响應头,確認没有被中間层改寫。
- 從服務器日誌中篩選爬虫 UA 與 304 狀態碼,統計回訪中 304 的比例。
- 對比例異常的 URL,检查模板里是否輸出了時間、随机數或未登入態内容。
與 URL 發現的關系
缓存协商不直接决定某個 URL 是否被發現,但它影响蜘蛛在同一時間窗口里能處理多少請求。当大量本可返回 304 的頁面被迫返回完整正文时,响應時間變長,蜘蛛在同一时段能覆盖的 URL 數量就會下降,新入口的發現节奏也可能被拖慢。反過来,如果為了追求 304 而把内容更新藏在客戶端渲染里,蜘蛛拿到的 HTML 長期不變,連結入口同样會缺失。两者需要平衡。
配置时的注意点
- ETag 應该由内容决定,而不是由請求决定。
- Last-Modified 應反映内容真實變更時間,不要用部署時間批量刷新所有頁面。
- 對确實频繁更新的頁面,允许返回 200,不必强求 304。
- 301、404 等狀態碼不要返回 304,狀態语义要分開。
- 在 sitemap 的 lastmod 與頁面 Last-Modified 之間保持一致,避免互相矛盾的信号。
缓存协商是抓取路径上一個不起眼但可观测的环节。定期看日誌里的 304 比例和响應時間,比猜测蜘蛛為什么不来更有依據。