10分钟帮你理顺浏览器HTTP缓存规则

1,137 阅读6分钟

前置知识-HTTP报文

报文分两种

  • 请求(Request)报文

    请求行

    HTTP头

    请求报文主体(仅POST请求报文主体)

  • 响应(Response)报文

    请求行

    HTTP请求头

    响应报文主体

前置知识-缓存过程

客户端与服务端通信方式为应答模式即浏览器发起请求,服务器响应请求。所以浏览器第一次发起请求时,浏览器会根据请求报文的HTTP头的缓存标记判断是否启用缓存结果,如果没有缓存结果就发送真实请求,并把响应的数据根据缓存标记存入浏览器缓存,待下次使用。

结论:

  • 浏览器每次发送请求报文之前会先根据缓存标记查看浏览器是否已有数据的缓存
  • 浏览器每次拿到响应报文之后也会根据缓存标记将数据缓存起来

上面两个操作就确保了每个请求与缓存的写入读取关系。只要再理解是如何根据缓存标记存入读取缓存的就完全理解了浏览器使用规则。

强制缓存

强制缓存就是在发送真实HTTP请求报文之前直接从浏览器拿去数据。强制缓存分为三种情况。

  • 不存在缓存结果和缓存标记,强制缓存失败,直接发起真实HTTP请求,向服务器请求数据。

  • 存在缓存数据和缓存标记,但已失效,强制缓存失败,则尝试使用协商缓存(协商缓存下文表)

  • 存在缓存数据和缓存标记,未失效,命中强制缓存,直接返回缓存结果,不发生实际HTTP请求。

强制缓存方式已知,那么规则是什么呢?

当服务端返回的响应包含强制缓存HTTP头,字段分别叫Expires(译:失效)和Cache-Control(缓存管理),其中Cache-Control的优先级比Expires更高。

Expires

Expires是HTTP/1.0版本控制缓存的字段,对应的值是到期时间,即请求的时间小于Expires的值则使用缓存数据。

Cache-Control

Expires是HTTP/1.1版本控制缓存的字段,作为替代Expires的字段,产生的原因是Expires使用的是客户端时间与服务端返回的时间做对比,这样就产生了误差(例如:时区不同;客户端/服务端时间不准确),强缓存会直接失败,为了修复这个BUG,在1.1版本中使用了新字段Cache-Control对BUG进行修复。当然在HTTP/1.1中Expires也可以使用,但是如果同时出现Cache-Contorl和Expires则Cache-Contorl优先级更高。

Cache-Control字段的规则:

  • public:所有内容都缓存(客户端和代理服务器都可以缓存)
  • private:所有内容只有客户端可以缓存(默认值)
  • no-cache:客户端缓存内容,但是是否使用则使用协商缓存来验证
  • no-store:所有内容都不被缓存,也就是即不使用强制缓存也不使用协商缓存
  • max-age=xxx(xxx is number)缓存内容xxx秒后过期(修复了Expires的BUG)

🌰:

  • Expires的时间戳表示在这个时间点之前,都可以使用缓存数据
  • Cache-contorl的优先级比Expires的高,最终表示这个请求只要是在600s内再次请求,就直接使用缓存

缓存是如何在浏览器存储的呢?

  • from memory cache

    表示缓存是从内存中读取的。有两种特性:

    • 快速读取

      内存缓存将数据直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。

    • 时效性

      一旦该进程关闭,则该进程的内存则会清空。

  • from disk cache

    表示缓存是从硬盘中读取的。硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

基于上述两种缓存存储的特点,浏览器在刷新时会将js和img放入memory中,以便快速读取,css文件会存入disk中。而在关闭页面又打开页面时,所有资源都是从disk中缓存到的(标签页删除后,资源就不会存在于mrmory中)

协商缓存

协商缓存就是在强制缓存失败(缓存资源过期后)后再次发送请求,根据服务器的响应,来决定浏览器保存的缓存是否更新其实效性。若返回的状态码是304,则代表命中协商缓存。若未命中则会返回200。

控制协商缓存的HTTP头字段是Last-Modified/If-Modified-Since和Etag/If-None-Match。

那么协商规则又是什么呢?

  • HTTP/1.0

    • Last-Modified(最后修改时间)

      • 返回的是资源最后修改的时间

    • If-Modified-Since

      • 触发协商缓存的时候,就会将第一次请求响应的Last-Modified带入到If-modified-Since标记的值中,服务端发现有If-modified-since字段就知道了浏览器缓存的资源是什么时候的资源了,再根据当前文件的最后修改时间做对比,如果可以使用缓存则返回304,浏览器使用缓存数据。如果浏览器缓存失效,则返回200,最新的数据。

  • HTTP/1.1

    因为客户端和服务端的时间点可能会产生时区等差异,故在1.1版本中引入优先级更高的Etag/If-None-Match修复了Last-modified/If-Modified-Since可能产生的问题。

    • Etag

      返回服务器给定的当前资源的唯一标识符

    • If-None-Match

      将第一次请求的Etag当值返回给服务端,服务端发现有if-none-match字段,就知道浏览器缓存的是什么版本的资源了,再根据当前版本资源的唯一标识符,就可以判断浏览器是否可以使用缓存数据,若可以则返回304,不能则返回200携带最新版本的资源。

总结

浏览器缓存规则:

通过强制缓存规则,在发送实际请求前,看是否能够命中浏览器缓存,如果没有缓存,发送真实请求。若存在缓存再根据expires表示的时间戳和cache-control表示的有效期判断缓存是否已过期,若已过期则发送请求携带HTTP头,HTTP/1.0使用If-Modified-Since值为第一次请求获取的Last-Modified的值,HTTP/1.1为了修复1.0的时间戳bug,使用唯一标识符if-none-match,其值为第一次请求发送过来的Etag的值。如果浏览器缓存有效,则返回304,如果没有已过期,则返回200,携带最新的资源。若未过期,则使用浏览器缓存。

浏览器缓存机制流程图如下: