Skip to content

浏览器缓存机制

浏览器的缓存机制是提升性能的重要手段,通过缓存资源减少重复的网络请求,降低带宽消耗,加快页面加载速度。

浏览器在请求资源时,通常会依赖缓存来决定是否需要重新从服务器获取资源。

相关HTTP标头

在讲解缓存的具体机制之前,先了解一下与缓存相关的HTTP标头。此处的字段和具体字段内容并不全面,只是列举了一些与本篇文章相关的内容。

  • Expires

    HTTP/1.0 引入的字段,用来指定资源的过期时间。它使用的是绝对时间,表示资源在这个时间之前有效。

    http
    Expires: Wed, 21 Oct 2023 07:28:00 GMT

    NOTE

    但是,Expires 字段存在一个问题,就是服务器返回的时间是服务器的时间,这在客户端和服务器时间不同步的情况下可能会出现问题


  • Cache-Control (与 Expires 同时存在时,该优先级更高)

    HTTP/1.1 引入的字段,主要用来定义资源在客户端或代理服务器(如 CDN)中的缓存策略。它可以指定缓存的存储策略、过期时间、私有或共享等。

    http
    max-age 表示该资源可以在客户端缓存 3600 秒(1 小时)
    在这段时间内不会向服务器重新请求。
    Cache-Control: max-age=3600  
    
    private 表示只能被私有缓存(如浏览器)缓存,不能被共享缓存。
    适用于个性化或用户相关的数据。
    Cache-Control: private 
    
    public 表示可以被共享缓存(如 CDN)缓存。
    Cache-Control: public
    
    no-store 表示不缓存该资源。
    Cache-Control: no-store
    
    no-cache 表示可以使用缓存,但必须先向服务器发起请求验证资源是否有效(通过 ETag 或 Last-Modified 头验证)。
    Cache-Control: no-cache

  • Last-ModifiedIf-Modified-Since

    Last-Modified 是服务器响应头,表示资源的最后修改时间。 当资源超过 max-age 时间后,浏览器会在下一次请求该资源时,通过 If-Modified-Since 头字段将这个时间发送给服务器,询问资源是否自那时以来有过修改。

    如果没有修改,服务器返回 304 Not Modified,浏览器可以继续使用本地缓存。同时更新资源的过期时间。

    http
    HTTP/1.1 200 OK
    Content-Type: text/html
    Content-Length: 1024
    Date: Tue, 22 Feb 2022 22:22:22 GMT
    Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
    Cache-Control: max-age=3600
    
    <!doctype html>
    
    http
    If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT

    NOTE

    • Last-Modified 的精度是秒级,因此对于频繁修改的资源可能不够精确。

    • 如果资源是动态生成的(即使内容没有变化,每次生成时间也会不同),Last-Modified 可能不适用。


  • ETagETag(Entity Tag)是服务器为每个资源生成的唯一标识符,用于标识资源的版本或内容。 与 Last-Modified 不同,ETag 可以是任何形式的标识符(例如hash值),不依赖时间戳,精度更高。

    浏览器在请求资源时,会在请求头中携带 If-None-Match 字段,该字段的值就是之前资源的 ETag

    服务器通过比较 ETag 来判断资源是否发生变化。如果没有变化,返回 304 Not Modified;如果变化了,则返回新的资源。

    http
    HTTP/1.1 200 OK
    Content-Type: text/html
    Content-Length: 1024
    Date: Tue, 22 Feb 2022 22:22:22 GMT
    ETag: "deadbeef"
    Cache-Control: max-age=3600
    
    <!doctype html>
    
    http
    GET /index.html HTTP/1.1
    Host: example.com
    Accept: text/html
    If-None-Match: "deadbeef"

缓存的种类

浏览器缓存主要分为两种:私有缓存共享缓存

私有缓存

私有缓存由Cache-Control: private控制,仅作用于当前用户的浏览器,不会被共享缓存(如 CDN)缓存。

私有缓存相当于服务器基于一定规则向浏览器发放的一份独立资源,只有当前用户可以使用,其他用户无法使用。 因为每个用户的缓存是独立的,私有缓存适合用来缓存个性化的内容或与用户相关的敏感信息,确保不会因为缓存共享而导致数据泄露。

共享缓存

共享缓存(Shared Cache)是指多个用户或多个客户端可以共同使用的缓存,通常用于减少重复的网络请求,提高性能,减轻服务器负担。

它存在于中间代理、内容分发网络(CDN)、企业网关或其他公共缓存服务器上。

目的是将资源缓存到靠近多个客户端的地方,使得这些客户端可以共享同一份资源副本,而不必每次都从原始服务器获取资源。

根据具体的应用场景不同可以分为以下两种共享缓存

  • 代理缓存:通常位于企业、组织或网络服务提供商的代理服务器上。 适合内部有大量用户访问相同资源的情况。 减少了对外部网络的依赖,降低了带宽使用,提高了资源获取的速度,尤其是在慢速网络环境下。

  • 托管缓存:通常是由专门的托管服务(例如 CDN)提供的缓存服务,是一种专门用于加速内容传递的网络。 适用于大规模、全球性用户访问的应用场景。 通过将内容缓存到靠近用户的位置,减少网络延迟,提高访问速度。

启发式缓存

在正式讲解浏览器缓存机制的流程之前,先了解一下启发式缓存(Heuristic Caching)。

启发式缓存是用于处理那些没有明确提供缓存控制头(如 Cache-ControlExpires)的资源。 这种推测机制虽然不如明确的缓存指令精确,但可以在某些情况下提高性能,避免频繁向服务器请求相同的资源。

常见的启发式方法是使用 Last-Modified 到请求时间的间隔作为参考,通常浏览器会缓存该资源一段时间(例如,这个间隔时间的 10% 或其他固定比例)。

如果该资源 10 天前修改过,而没有明确的缓存控制字段,浏览器可能会缓存该资源 1 天(10 天的 10%)。

启发式缓存是在 Cache-Control 被广泛采用之前出现的一种解决方法,基本上所有响应都应明确指定 Cache-Control 标头。

浏览器缓存机制

浏览器的缓存判定通常有两个过程:强缓存协商缓存

强缓存不需要向服务器发送请求,首先检查本地缓存中是否有该资源的缓存副本,并且检查缓存是否未过期。

这种机制通过 HTTP 响应头中的 Cache-Control(具体为其中的 max-age 值) 或 Expires 来实现。

如果强缓存失效,将进入协商缓存的流程。

浏览器会通过 If-Modified-SinceIf-None-Match 请求头带上 Last-ModifiedETag 的值,请求服务器验证缓存是否仍然有效。

如果服务器响应 304 Not Modified,浏览器会使用本地缓存资源,同时更新强缓存的有效时间。

如果服务器响应 200 OK,则返回新的资源,并更新缓存。

基于 Apache-2.0 许可发布