浏览器缓存 强缓存
Expires: Expires是http1.0提出的一个表示资源过期时间的header,它描述的是一个绝对时间,由服务器返回。Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效 1 Expires: Wed, 11 May 2018 07:20:00 GMT
Cache-Control: Cache-Control 出现于 HTTP / 1.1,优先级高于 Expires ,表示的是相对时间 1 Cache-Control: max-age=315360000
题外tips Cache-Control: no-cache不会缓存数据到本地的说法是错误的,详情《HTTP权威指南》P182 Cache-Control: no-store才是真正的不缓存数据到本地 Cache-Control: public可以被所有用户缓存(多用户共享),包括终端和CDN等中间代理服务器 Cache-Control: private只能被终端浏览器缓存(而且是私有缓存),不允许中继缓存服务器进行缓存
协商缓存 当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串 协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的
Last-Modified,If-Modified-Since Last-Modified 表示本地文件最后修改日期,浏览器会在request header加上If-Modified-Since(上次返回的Last-Modified的值),询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来 但是如果在本地打开缓存文件,就会造成 Last-Modified 被修改,所以在 HTTP / 1.1 出现了 ETag
ETag、If-None-Match
Etag就像一个指纹,资源变化都会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的 If-None-Match的header会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来
ETag的优先级比Last-Modified更高
具体为什么要用ETag,主要出于下面几种情况考虑:
一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
某些服务器不能精确的得到文件的最后修改时间。
启动nginx实验 纸上得来终觉浅,亲自动手才能记得更牢。下面跟着我的节奏,来一场快速的浏览器缓存实验。
测试页面 首先明确我们的需求,设计如下html。包含一个标准文档和一个jpg静态资源。
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > title</title > </head > <body > <div > test</div > <img src ="./avatar.jpg" alt ="" > </body > </html >
添加nginx.conf开始测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 新建nginx.conf文件 server { listen 10000; server_name localhost; index index.html; root /usr/share/nginx/html; location / { etag off; add_header Last-Modified ''; index index.html; } location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { etag off; add_header Last-Modified ''; } }
因为nginx 默认开启了etag 和Last-Modified,我们先把他去掉看效果
nginx on! 1 2 3 docker run -v /path/to/your/html:/usr/share/nginx/html \ -v /path/to/your/nginx.conf:/etc/nginx/conf.d/mysite.conf \ -p 10000:10000 --name nginx-test nginx
新建一个无痕浏览器窗口,打开localhost:10000;多次刷新页面依然之后查看请求详情
并且执行 docker logs -f nginx-test
能发现每次服务器都收到了请求日志。
可见没有设置缓存header的情况下,浏览器还真是一点缓存效果都没有!
接下来我们添加一些强缓存的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 server { listen 10001; server_name localhost; index index.html; root /usr/share/nginx/html; limit_rate 50k; #限制速度50K etag off; # 关闭协商缓存 add_header Last-Modified ''; # 关闭强缓存 expires 30s; location / { index index.html; } location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { } }
执行docker restart nginx-text
;刷新页面发现:
图片的缓存已经生效,并且有效期为30秒。因为docker没有设置时区,所以我本地的时间是大于expires的,但是缓存还是生效了,所以证明Cache-Control的优先级大于expires。使用浏览器强制刷新能忽略缓存时间,重新拉取资源。
对于文档文件,每次刷新都会带上Cache-Control: max-age=0
,强制刷新时会带上Cache-Control: no-cache;Pragma: no-cache
,并且服务器一直都有请求日志。所以对文档设置expires和cache-control无效?(一种猜测)
接下来我们再添加一些协商缓存的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 server { listen 10000; server_name localhost; index index.html; root /usr/share/nginx/html; limit_rate 50k; #限制速度50K etag off; # 关闭协商缓存 expires 10s; location / { index index.html; } location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { } }
docker restart nginx-test
之后刷新几次发现
文档文件协商缓存生效,但是资源文件还是使用强缓存原因是:
如果Expires,Cache-Control: max-age,或 Cache-Control:s-maxage都没有在响应头中出现,并且设置了Last-Modified时,那么浏览器默认会采用一个启发式的算法,即启发式缓存。通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间。
强制刷新时不会带上If-Modified-Since,所以直接从服务端取文件。 修改index.html文件之后request的If-Modified-Since小于服务器中记录的时间,于是返回了新文档,状态码200,并且更新了response的Last-Modified.
ETag流程和Last-Modified的表现几乎一致,就不多赘述了。
最后,同时使用强缓存和协商缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 server { listen 10000; server_name localhost; index index.html; root /usr/share/nginx/html; expired 30s; location / { index index.html; } location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { } }
刷新时,文档304,资源200 from cache。30秒后强缓存失效,文档仍然304,资源也变为304,之后再变为200.
缓存的升级方案: service worker + caches api;