如何控制浏览器缓存

WEB开发缓存优化第一原则:缓存离用户越近,性能越高。无疑,浏览器是离用户最近的。那么如何控制浏览器缓存呢? 我以前错误的以为,如果没有显式告知浏览器缓存,那么浏览器就不会缓存,这引起了很大问题。

1 Last-Modified

如果没有指定 ExpiresCache-Control 但是指定了 Last-Modified ,浏览器会猜应该缓存多久。对多数浏览器而言,缓存的时长是 (现在时间 - Last-Modified时间)* 10% 。越老的文件缓存的时间越长。这种控制缓存的方式相当任性,最好显式指定缓存。

2 Cache-Control 和 Expires

可以通过 Cache-ControlExpires 显式的指定缓存。 Cache-Control 的优先级高于 ExpiresCache-Control 指定相对时间,而 Expires 指定绝对时间。例如:

  1. Cache-Control: max-age=0 不缓存
  2. Expires: 0 不缓存
  3. Cache-Control: max-age=60 缓存60S
  4. Expires: Mon, 27 Feb 2017 05:08:53 GMT 在GMT时间2017-2-27 05:08:53过期

除了过期时间,*Cache-Control* 还有其它选项,Cache-Control参考

3 200 (from disk cache)、(from memory cache)

在chrome开发者工具中会看到 Status 200 Size (from disk cache) Size (from memory cache) ,这个的含义是:从浏览器缓存中读取数据,不会真的访问server端。性能最高。

4 304

如果缓存已经过期,会重新请求server端。如果缓存中有 ETag ,那么往server端发请求时会带着 If-None-Match ,值和 ETag 一致。如果缓存中有 Last-Modified ,那么往server端发请求时会带着 If-Modified-Since ,值和 Last-Modified 一致。 If-None-Match 优先级高于 If-Modified-Since ,如果服务端判定缓存仍然有效,返回304。虽然304对服务端有真实的请求,但是响应没有BODY,性能也很高。

5 实践

对于静态资源,推荐的做法是使用版本或签名作为文件名,更改内容时更新版本和签名,这样可以在浏览器端设置很长的过期时间。但是对于html,文件名不应该改变,应该通过减少缓存时间,甚至不设置缓存。

6 nginx 配置

# cache-control for static resource
set $cache_control "";
if ($uri ~ "\.(?:html|json)$") {
  set $cache_control "public, max-age=0, must-revalidate";
}
if ($uri ~ "\.(?:jpg|jpeg|gif|png|ico|svg|mp4|ttf)$") {   # 100 days
  set $cache_control "public, max-age=8640000";
}
if ($uri ~ "\.(?:css|js)$") {   # 1 day
  set $cache_control "public, max-age=86400";
}
add_header Cache-Control $cache_control;

7 浏览器如何请求资源

浏览器请求资源的方式影响缓存的使用(浏览器间有不同),资源分主资源和子资源。直接在地址栏输入的是主资源,主资源自己请求的资源是子资源。看例子, api/test 设置了 Cache-Control: max-age=60,private ,但是没有设置 ETagLast-Modifiedtest.html 是静态页面,它包含了 api/test 子资源,另外 XHR 请求也属于子资源请求。

  1. http://dev/api/test
  2. http://dev/test.html 内容是
<script src="http://dev/api/test"></script>
<a href="http://dev/api/test">Test</a>

在浏览器的地址栏输入上面的两个地址:其中1没有使用缓存,虽然 api/test 设置了缓存,但是并没有使用(因为是主资源,所以总是发请求,且没有 ETagLast-Modified 所以没有返回304)。2包含了两个资源,再次刷新,test.html 返回304(因为它是主资源,所以总是请求服务端), api/test 使用缓存(在chrome中)。但是在firefox中 api/test 也没有使用缓存,只有点击Test打开连接时 api/test 才使用了缓存。

除了在地址栏输入地址,点击刷新按钮,摁F5,还可以通过 Ctl-F5 强制刷新,此时所有的缓存都被忽略(老版本的IE不支持)。

了解这些并没有太多用,但是如果你想测试接口的缓存效果,发现每次刷新都没有使用缓存,可能是浏览器请求资源方式的缘故。

8 Cache-Control 注意事项

8.1 max-age=0,no-cache,no-store 的区别

  • max-age=0 的意思是缓存,但是不使用它,当从服务端获取资源失败时,可以考虑使用。
  • no-cache 的意思是缓存,但是不使用它,仅当从服务端返回了304时,才使用。
  • no-store 的意思是不缓存。

8.2 private

private 对浏览器没有太多意义,更多的是针对CDN或充当CDN的nginx之类,它告诉CDN不要缓存此类响应。例如:同一个url,针对cookie中的用户不同,返回不同的内容,应该是private的,此时CDN就不应该缓存。

8.3 浏览器请求时带着:max-age=0

这是告诉CDN不要使用缓存的内容,把请求代理到upstream服务器。

9 浏览器之间的差异

各家浏览器基于自己对缓存和性能的理解,对缓存的处理不同,以上描述不是对所有浏览器都适用,但没有本质影响。

10 参考资料

Author: root

Created: 2017-03-15 三 12:34

Validate