蜘蛛抓取一个页面,并不总是把整份 HTML 重新下载一遍。如果站点支持条件请求,服务器就有机会只回一个 304,双方都省事:蜘蛛少下载一份重复内容,服务器少传一次响应体。这个机制的开关,就藏在响应头里。
条件请求:蜘蛛会带着凭证来
当爬虫抓到过某个 URL 之后,再次访问时通常会在请求头里带上 If-Modified-Since(时间)或者 If-None-Match(ETag 值)。服务器拿到这两个值,与当前资源版本比对:没变就返回 304 Not Modified,响应体为空;变了就返回 200 和新内容。
很多人只关注 200 和 404,忽略了 304。其实从日志里看 304 的占比,能判断站点是否在做有效的版本管理。如果一个页面几个月没改,蜘蛛每次来都拿到 200 全量传输,这部分带宽和响应时间多少有些浪费。
Last-Modified 与 ETag 别互相打架
Last-Modified 依赖文件的修改时间,对静态文件很自然;ETag 是服务端算出的版本标识,可以是内容哈希,也可以是别的规则。两者可以同时存在,但要注意一致性。
- 内容没变,Last-Modified 却每次请求都在变,比如动态写入的当前时间戳,蜘蛛就会认为页面一直在更新。
- ETag 用了不稳定的值,比如进程 ID、随机数,条件请求会失效,全部退化成 200。
- 两个头都给的时候,一般优先判断 ETag;两者结论冲突时行为容易混乱。要么只留一个,要么保证它们同进同退。
动态页面的常见坑
列表页、搜索结果页、带时间戳的模板,很容易无意间把当前时间写进 Last-Modified。看上去没什么,实际效果是蜘蛛每次来都拿到 200,还可能因为误判内容频繁更新而提高抓取频率,反而占用了别处的预算。要么干脆不做协商缓存,要么用真实的最后编辑时间。
Cache-Control 是给谁看的
Cache-Control 主要约束浏览器和中间缓存,但对蜘蛛也有间接影响。常见几种写法:
- max-age 较长,配合内容指纹:适合图片、CSS、JS 这类不会原地修改的资源。
- HTML 文档一般给较短的 max-age,或者用 no-cache,即可以缓存但每次要回源校验,保证改版后较快生效。
- no-store 是完全不缓存,用在下发敏感信息的页面上没问题,套在普通内容页就有点浪费。
需要留意的是,no-cache 并不等于不缓存,它只是要求每次向服务器确认;真正禁止存储的是 no-store。这两个词写混,是排查缓存问题时最常见的误会。
CDN 这一层要单独确认
开启 CDN 之后,响应头可能来自边缘节点而不是源站。这里容易出现几类问题:
- 把 301、404 甚至 500 的响应也缓存了很久,源站修好了,蜘蛛拿到的还是旧的错误状态。
- 缓存了带 Set-Cookie 的页面,或者把登录态页面缓存成了公共版本。
- 各节点缓存版本不一致,同一 URL 在不同地区返回内容不同,蜘蛛拿到的版本随机。
建议对 HTML 文档的缓存规则保守一些,错误状态码不缓存或者只缓存很短时间,静态资源再放开。
怎么检查和验证
- 用 curl -I 或浏览器网络面板查看响应头,确认是否有 ETag、Last-Modified、Cache-Control。
- 连续请求两次,看第二次是否返回 304;如果始终是 200,检查版本标识是否稳定。
- 在服务器日志里统计 304 与 200 的比例,结合响应时间看有没有实际收益。
- 修改一次正文内容,确认版本标识同步变化,蜘蛛下次来访能拿到新内容。
缓存和 304 的目标是少传重复数据,而不是把更新藏起来。省流量的前提是:内容变了,蜘蛛一定看得出来。