如何控制浏览器缓存
WEB开发缓存优化第一原则:缓存离用户越近,性能越高。无疑,浏览器是离用户最近的。那么如何控制浏览器缓存呢? 我以前错误的以为,如果没有显式告知浏览器缓存,那么浏览器就不会缓存,这引起了很大问题。
1 Last-Modified
如果没有指定 Expires 或 Cache-Control 但是指定了 Last-Modified ,浏览器会猜应该缓存多久。对多数浏览器而言,缓存的时长是 (现在时间 - Last-Modified时间)* 10%
。越老的文件缓存的时间越长。这种控制缓存的方式相当任性,最好显式指定缓存。
2 Cache-Control 和 Expires
可以通过 Cache-Control 和 Expires 显式的指定缓存。 Cache-Control 的优先级高于 Expires 。 Cache-Control 指定相对时间,而 Expires 指定绝对时间。例如:
- Cache-Control: max-age=0 不缓存
- Expires: 0 不缓存
- Cache-Control: max-age=60 缓存60S
- 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
,但是没有设置 ETag 和 Last-Modified , test.html
是静态页面,它包含了 api/test
子资源,另外 XHR 请求也属于子资源请求。
http://dev/api/test
http://dev/test.html
内容是
<script src="http://dev/api/test"></script> <a href="http://dev/api/test">Test</a>
在浏览器的地址栏输入上面的两个地址:其中1没有使用缓存,虽然 api/test
设置了缓存,但是并没有使用(因为是主资源,所以总是发请求,且没有 ETag 和 Last-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 浏览器之间的差异
各家浏览器基于自己对缓存和性能的理解,对缓存的处理不同,以上描述不是对所有浏览器都适用,但没有本质影响。