站点运营

站点运营:304 与缓存头,蜘蛛再来时该给什么响应

蜘蛛第二次访问同一个页面,服务器该返回 200 还是 304?本文从条件请求说起,讲清 Last-Modified 与 ETag 的配合方式、Cache-Control 的常见写法,以及多机部署和 CDN 场景下缓存头容易踩的坑,并给出可操作的验证步骤。

站点运营

站点运营:304 与缓存头,蜘蛛再来时该给什么响应

蜘蛛抓取一个页面,并不总是把整份 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 文档的缓存规则保守一些,错误状态码不缓存或者只缓存很短时间,静态资源再放开。

怎么检查和验证

  1. 用 curl -I 或浏览器网络面板查看响应头,确认是否有 ETag、Last-Modified、Cache-Control。
  2. 连续请求两次,看第二次是否返回 304;如果始终是 200,检查版本标识是否稳定。
  3. 在服务器日志里统计 304 与 200 的比例,结合响应时间看有没有实际收益。
  4. 修改一次正文内容,确认版本标识同步变化,蜘蛛下次来访能拿到新内容。
缓存和 304 的目标是少传重复数据,而不是把更新藏起来。省流量的前提是:内容变了,蜘蛛一定看得出来。