浏览器缓存与相关 HTTP Header

在 HTTP 请求中,内容由 Web 服务器产生,而浏览器缓存内容存储在浏览器本地,他们之间不可能独立完成整个请求过程,所以这个沟通机制就叫缓存协商。

而缓存协商,毕竟还需要浏览器与 Web 服务器的一次 HTTP 请求响应头的确认,如何彻底的消灭请求,就需要用到浏览器缓存。

缓存协商

与缓存协商相关的两个重要 Header 标记是以下两个:

  • Last-Modified

静态文件可以通过 stat() 系统调用它在物理文件系统的最后时间,一般 Web 服务器会为静态文件 HTTP 响应头自动生成最后修改时间。实际动态脚本如 PHP 一般不存在传统意义上的最后修改时间,不过可以人为的输出响应头信息,用于表示数据的最后修改时间。

当响应头中包含 Last-Modified 时,浏览器下次请求会增加 If-Modified-Since 的请求头,从而产生了缓存协商。浏览器会向 Web 服务器询问某时间以后资源是否修改过,如果满足要求,如Web 服务器会返回 304 Not Modified 的状态码。此时并不会返回 HTTP 实体内容。

协议示例:

Last-Modified: Mon, 01 Aug 2016 16:20:06 GMT
If-Modified-Since: Mon, 01 Aug 2016 16:20:06 GMT
  • ETag

HTTP/1.1 中支持的另一种缓存协商方法,工作原理与 Last-Modified 类似,不同的是通过一串编码标识内容是否有更新,浏览器下次请求会增加 If-None-Match 请求头来进行缓存协商。

主要使用场景比如在多台 Web 服务器负载均衡的情况下,每个服务器很难保证最后修改时间一致,但文件确实一致的。ETag 就可以避免这个问题。

协议示例:

Etag: "579f76b6-7187"
If-None-Match: W/"579f76b6-7187"

当 HTTP 协议中包含上述两种缓存协商协议时,两者是与的关系,即需要同时满足两者才能协商成功,返回 304 状态码。

浏览器本地缓存(过期时间)

RFC2616 中针对 HTTP/1.1介绍说,缓存的目的在于某些场景下彻底消灭请求。如何彻底的消灭请求,有以下两个重要的 Header 标记:

  • Expires

Expires 格式类似Last-Modified,它指示了内容过期的绝对时间,当浏览器看到 Expires 标记时,会拥有极大的权利,无需在过期之前每次都询问服务器,而直接使用本地缓存。此时如果使用浏览器监听网络请求,会出现灰色的 HTTP 200 OK (from cache) 等状态,(注意是灰色的)

协议示例:

Expires: Wed, 21 Sep 2016 05:33:26 GMT
  • Cache-Control

如果用户浏览器本地时间与服务器时间不一致,或者服务器时间本来就是错的,Expires 可能就会存在问题,所以 HTTP/1.1 另一个标记用来弥补 Expires 的不足,那就是 Cache-Control

Cache-Control 指定了缓存过期的相对时间,单位是秒,并且这个时间是相对于浏览器本地时间,对于 Web 服务器而言,如 Nginx,当开启了 Expires 的同时,也会自动添加相应的 Cache-Control

协议示例:

Cache-Control: max-age=604800

目前主流浏览器都将 HTTP/1.1 作为首选,所以当 HTTP 响应头同时含有 Expires 和 Cache-Control 时,浏览器会优先考虑 Cache-Control。

Nginx 配置 Expires 与 Cache-Control 示例:

location ~* \.(gif|jpg|jpeg|png|bmp|swf|ico)$
{
    expires      30d;
    log_not_found off;
    access_log off;
}

浏览器如何请求页面

浏览器如何请求页面主要有以下几种:

  • 超链接、书签栏、转到按钮、地址栏回车

使用最多的一种方式,这样请求页面,会使用浏览器本地缓存,即 Expires 与 Cache-Control 仅对这种请求形式有效,返回结果为: 灰色的 HTTP 200 状态码 (注意是灰色的)

  • F5、浏览器刷新按钮

一般的刷新,它允许浏览器在请求中附加必要的缓存协商,即此时能让 Last-Modified 与 ETag 发挥效果,但不允许浏览器直接使用本地缓存,返回结果为:HTTP 304 状态码

  • Ctrl+F5、禁用浏览器缓存的刷新

这种方式可以叫强制刷新,会忽略缓存协商本地浏览器缓存,直接向 Web 服务器发送请求,实际普通用户很少会这样操作,返回结果为: HTTP 200 状态码

实际强制刷新是主要是在请求中传递了Cache-Control请求头,因为Cache-Control属于HTTP的通用首部:

Cache-Control: no-cache
Cache-Control: max-age=0

CDN 缓存配置

当您的网站访问量过大,一般会使用 CDN 进行加速,这时在 Web 服务器之前又多了一个 CDN 缓存时间,具体如何配置CDN 的缓存时间呢,每个 CDN 厂商都有不同的标准。

但一般都会通过参照源服务器配置的浏览器过期时间这项配置来决定 CDN Cache时间,当然有时还可以自行配置,可以通过下面链接查看阿里云 CDN 的缓存策略:

https://help.aliyun.com/document_detail/27136.html