蜘蛛抓取頁面时,服務器並不需要每次都把完整 HTML 重新發一遍。如果請求里带了驗證信息,而内容没有變化,返回一個 304 狀態碼就能結束這次抓取。這样既省服務器带宽,也减少蜘蛛等待時間。但很多站点在缓存驗證头上配置得比較随意,導致條件請求形同虚设,每次抓取都變成全量回源。
條件請求在做什么
浏览器和蜘蛛在第一次拿到頁面时,服務器通常會在响應头里给出两個驗證器:Last-Modified 和 ETag。下次再請求同一個地址时,客戶端會把這两個值分別放進 If-Modified-Since 和 If-None-Match。服務器比較後如果内容没變,就返回 304 Not Modified,不携带正文。
對蜘蛛来说,304 意味着“這個地址我看過了,内容還是老样子”,它可以很快轉向下一個地址。如果服務器總是返回 200 和完整正文,蜘蛛就得重新下载、重新解析,抓取效率會下降。
容易被忽略的几種失效情况
驗證器每次都在變
有些動態程序會把 Last-Modified 设成目前時間,或者用進程 ID、内存地址生成 ETag。這样每次請求得到的驗證器都不一样,條件請求永遠對不上,304 自然不會出現。
多节点或多层代理不一致
站点有多台源站或经過反向代理、压缩层时,不同节点给出的 ETag 可能不同。蜘蛛這次請求落到 A 节点,下次落到 B 节点,驗證失敗,又會拿到 200 响應。
压缩层改寫了 ETag
開啟 gzip 或 Brotli 後,响應体的字节變了,但 ETag 如果還是按原始内容計算,就可能出現驗證不匹配。部分服務器會改用弱 ETag(W/ 前缀),這本身没問题,但要確認整個鏈路行為一致。
Cache-Control 與驗證头冲突
Cache-Control 里寫了 no-store 或极短的 max-age,同时又依赖 ETag 做驗證,會让中間缓存直接放弃存储,條件請求也失去意义。
304 响應里带了正文
按規范,304 不應包含消息体。如果程序在 304 後面仍然輸出頁面内容,客戶端可能解析異常,浪費一次請求。
自查步骤
- 用 curl -I 连續請求同一個 URL 两次,第一次记下 Last-Modified 和 ETag。
- 第二次請求时手動带上 If-Modified-Since 或 If-None-Match,观察是否返回 304。
- 如果返回 200,對比两次的 ETag 是否變化,以及 Last-Modified 是否等于目前時間。
- 在多台源站或多條线路下重复測試,確認驗證器一致。
- 在訪問日誌里統計 304 的比例。比例長期接近零,通常說明條件請求没有生效。
- 換用蜘蛛 UA 再测一次,排除按 UA 差异化輸出導致驗證器變化。
調整方向
- ETag 優先使用内容哈希或稳定的版本号,不要用時間戳、進程号、内存地址。
- Last-Modified 取自内容最後修改時間,而不是請求發生時間。
- 源站與代理层统一 ETag 生成規則,压缩层不要随意改寫强 ETag。
- 對長期不變的静態资源,設定較長的 Cache-Control,並保留 ETag 供驗證。
- 對频繁更新的頁面,可以缩短 max-age,但不要關閉條件請求。
- 304 响應只返回头,不輸出正文。
和抓取预算的關系
抓取预算有限时,服務器响應速度、狀態碼、内容是否變化都會影响蜘蛛繼續訪問的意愿。304 不能直接提升排名,但能减少重复传輸,让蜘蛛把時間花在真正變化過的地址上。對内容量大、更新频繁的站点,效果更明顯。
抽查时建议固定几個代表性地址:首頁、栏目頁、詳情頁、静態资源,分別记錄狀態碼、驗證器和响應体积,隔一段時間再對比,看是否有节点漂移。
條件請求属于服務器與协议层面的基础配置,改動不大,但需要定期確認。把它纳入站点巡检清單,和日誌、狀態碼、缓存策略一起看,能避免很多“蜘蛛来了却白跑一趟”的情况。