浏览器缓存机制与Nginx配置调优

HTTP状态码

200 (form memory cache) 不请求网络资源,从内存中读取资源,一般脚本、字体、图片会存在内存当中

200 (form disk cache) 不请求网络资源,从磁盘中读取资源,一般非脚本会存在内存当中,如css等

200 (1kb) 从服务器下载最新资源. (1kb即资源的大小)

304 (1kb) 请求服务端发现资源没有更新,使用本地资源. (1kb即报文的大小)

以上是chrome在请求资源是最常见的几种http状态码

前两种200状态码是不需要浏览器请求服务器的, 直接从本地读取资源, 所以速度是最快的, 而304状态码也是缓存, 只不过需要浏览器先请求服务器, 这种是协商缓存

那么浏览器究竟在什么情景下会返回以上几种不同的状态码呢?

答案很简单, 在浏览器第一次请求某一个URL时,服务器端会返回200状态码,内容是客户端请求的最新资源,同时服务端可以主动设置Response Headers里的last-modified的属性标记此资源在服务器端最后被修改的时间, 还可以设置cache-control属性和etag属性以及expire属性来告诉浏览器下次的请求的处理方式, 那么下文将依次讲解这些属性的作用

last-modified

last-modified格式类似这样:

last-modified : Fri , 12 May 2006 18:53:33 GMT

客户端第二次请求此URL时,根据HTTP协议的规定,浏览器会向服务器发送if-modified-since报头,询问该时间之后文件是否有被修改过:

if-modified-since : Fri , 12 May 2006 18:53:33 GMT

如果服务器端的资源没有变化,则返回 304(Not Changed)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

Nginx默认开启last modified特性, 也可以主动配置:

?
1
if_modified_since off|on;
etag

HTTP协议规格说明定义etag为“被请求变量的实体值”。另一种说法是,etag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个HTML文档,但也可能是JSON或XML文档。

服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:

etag:"50b1c1d4f775c61:df3"

客户端的查询更新格式是这样的:

if-none-match : W / "50b1c1d4f775c61:df3"

如果etag的值没有发生改变,则返回 304(Not Changed)状态码,这也和last-modified一样。etag在断点下载时会非常有用。

Nginx1.7.3及以上的版本默认开启Etag特性, 但是和Last-Modified一样, 可以主动配置:

?
1
etag off|on;
cache-control

Cache-Control 是最重要的规则。这个字段用于指定所有缓存机制在整个请求/响应链中必须服从的指令。这些指令指定用于阻止缓存对请求或响应造成不利干扰的行为。这些指令通常覆盖默认缓存算法。缓存指令是单向的,即请求中存在一个指令并不意味着响应中将存在同一个指令。

简单来说, 当同时存在last-modified以及etag还有cache-control这个3个属性时, cache-control的优先级是最高的

cache-control有以下几个值:

public 所有内容都将被缓存

private 内容只缓存到私有缓存中

no-cache 所有内容都不会被缓存

no-store 所有内容都不会被缓存到缓存或 Internet 临时文件中

must-revalidation/proxy-revalidation 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证

max-age=xxx (xxx is numeric) 缓存的内容将在 xxx 秒后失效, 如果和Last-Modified一起使用时, 优先级更高

浏览器对以上几种值的不同表现:

1 打开新窗口:

public 会从缓存中读取资源, 也就是返回前两种200状态码

private, no-cache, no-store 都会重新访问服务器。

如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如:

cache-control: max-age=5(表示当访问此网页后的5秒内再次访问不会去服务器)

2 在原窗口按Enter键

public 会从缓存中读取资源, 也就是返回前两种200状态码

private或must-revalidate 则只有第一次访问时会访问服务器,以后就不再访问。

no-cache, no-store 每次都会访问。

如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器

3 点击刷新按钮

无论将cache-control设置为什么值,浏览器都会重新访问服务器

4 点击后退按钮

public、private、must-revalidate、max-age 都不会重新访问

no-cache 则每次都重复访问

Cache-Control是关于浏览器缓存的最重要的设置,因为它覆盖其他设置,比如 expires 和 last-modified。另外,由于浏览器的行为基本相同,这个属性是处理跨浏览器缓存问题的最有效的方法。

Nginx设置cache-control(例如设置max-age为30秒):

?
1
add_header  Cache-Control "max-age=30";
expires

Expires 头部字段提供一个日期和时间,响应在该日期和时间后被认为失效。失效的缓存条目通常不会被缓存(无论是代理缓存还是用户代理缓存)返回,除非首先通过原始服务器(或者拥有该实体的最新副本的中介缓存)验证。(注意:cache-control max-age 和 s-maxage 将覆盖 Expires 头部。)

Expires 属性接收以下格式的值:

Expires: Sun, 08 Nov 2009 03:37:26 GMT

如果查看内容时的日期在给定的日期之前,则认为该内容没有失效并从缓存中提取出来。反之,则认为该内容失效,缓存将采取一些措施。

以上几种属性的优先级

last-modified, etag, cache-control, expires 被服务端同时设置时, 浏览器的下次请求会做出如下判断:

cache-control 与 expires 控制浏览器是否从本地读取缓存, 并且cache-control会重写expires的规则。

例如cache-control设置了max-age, 浏览器则会先判断max-age,

如果cache-control没有设置max-age, 浏览器则会先判断expire, 再发送 Http 请求

服务器收到请求则会先判断资源的 last-modified,再判断 etag ,必须都没有过期,才能返回 304(Not Changed)状态码。

可能出现的问题

前面只讲了几种属性同时被设置后, 浏览器和服务器判断的优先级, 还有一种情况(也是最常见的情况)也是需要我们去考虑的

那就是服务端只设置了last-modified 以及 etag, 并没有设置cache-control和expires属性

为什么说这种情况是最常见的? 仔细想想还真是那么回事, 因为nginx默认开启了last-modified和etag属性

如果一只正常的后端汪只安装了环境, 并没有在缓存方面做一些配置, 那就是这种情况

这种情况下浏览器就不得不自行判断它应该将资源缓存多久。有些浏览器会将其缓存一天以上。

Google caching best practices guide 里说浏览器会根据 last-modified 自行推算缓存时长。

Firefox 的推算方法是:缓存时长 = (Date - last-modified) / 10

Chrome / Safari / IE 并没有公布他们的公式或算法。

此类文件的缓存时长通常取决于以下因素:

- 浏览器开辟的缓存空间大小

- 用户浏览过的站点数量和大小

- 用户是否关闭了浏览器

也就是说当服务端只配置了last-modified 以及 etag, 没有配置cache-control和expires的时候, 除非用户手动点击刷新按钮, 否则浏览器每次都会从本地缓存中加载, 而且这个本地缓存的缓存时间也是未知的

这就导致了一个问题, 比如说你没有做任何缓存的配置, 却发现每次更新了网站的PHP, HTML, JS, CSS文件, 非得让用户手动点击刷新按钮才能显示更新内容, 这可不是我们想要的!(被问我怎么知道的)

所以,你如果不想删掉 last-modified,etag, 还不想浏览器从本地加载缓存资源,就应该显示地指定cache-control或expires

下面是我网站的nginx的缓存配置(仅供参考):

1
2
3
4
5
6
7
8
9
#设置css/js/图片等静态资源的expires为30天
location ~ .*\.(css|js|ico|png|gif|jpg|json|mp3|mp4|flv|swf)(.*) {
    expires 30d;
}
 
#设置index.html的max-age为30秒
location /index.html {
    add_header  Cache-Control "max-age=30";
}

发布评论
还没有评论,快来抢沙发吧!