目录:网上冲浪指南

Safari 无法刷新缓存

2019/03/24

最近在开发的时候遇到了一个非常奇怪的问题,一个我没法拒绝的用户说文章保存后页面缓存没有被清除掉,我心中大惊,难道我又忘了去清除缓存了?于是赶紧去翻阅之前写的代码,发现我确实是做了处理的。然后我又想,难道是我又填错了缓存的 Key ?赶紧用 telnet 连上集群中的 memcached,测试了一下,发现缓存 Key 没有填错。后来我打开开发者工具看了一眼页面请求,发现响应头中有一句 Cache-Control: public,max-age=3600,于是我自己在开发机器上做了一下测试,只要强制刷新一下页面就行了。

不过事情并没有这么简单地结束,这位用户还是抱怨页面的内容仍然是被缓存的,做的更改并没有生效,不管怎么刷新都没有。之前在测试 memcached 缓存键的时候我就已经基本排除了服务端代码有问题的可能我写的代码怎么可能出错。没办法,我就只能重新看起来 MDN 的文档,看看 Cache-Control: public, max-age=3600 到底会对浏览器产生什么影响。

首先说说 public,这个指令的意思是允许资源被缓存在任意位置,例如服务端、反向代理、客户端。而 max-age=3600 则是说被缓存的资源在 3600 秒后过期。根据我以往的经验,在浏览器上强制刷新页面的时候,本地缓存应该会被忽略,所以这位用户遇到的问题可能是读取到了服务端响应的缓存。又得知用作反向代理的 Nginx 并没有使用任何与缓存相关的指令,所以这名用户读到的缓存很可能是被我的应用服务响应的。

那么现在让我们把实现转向到我在应用服务器使用的缓存组件: ResponseCaching。这是 ASPNET Core 自带的一个组件,用来实现缓存响应的功能。这个组件由两部分组成——ResponseCaching Feature 跟 ResponseCaching Middleware。ResponseCaching Feature 只是用来在响应头加上类似 Cache-ControlVary 这样的字段的工具,真正实现在服务端缓存响应的是 ResponseCaching Middleware。根据文档可知,ResponseCaching Middleware 只会缓存存储在 public 空间的响应。也就是说,这位用户的页面果然是被应用服务器缓存了。

但是我访问这个页面的时候难道就不会被应用服务缓存吗?我想,可能是我漏看了文档上的什么东西吧。很快,我就在文档上发现了这么一段话:

如果发现响应缓存中间件工作不正常,试试用 fiddle 或者 postman 这样的工具来进行测试,因为浏览器可能会在请求头上附加值为 no-cache 或者 max-age=0 这样的 Cache-Control 头,这时响应缓存中间件就不会使用缓存。

于是我检查了一下 Chrome 刷新页面时发送的请求头,果然包含了 Cache-Control: max-age=0,而当使用强制刷新的时候,请求头则会附加 Cache-Control: no-cache,面白い。不过这还是很难解释为什么这个用户总是能够访问到缓存。不过这时我想到,这名用户经常使用 Safari 浏览器,难道 Safari 浏览器刷新的时候不会带上这种请求头吗?于是我只好重启到了 MACos 进行了测试,结果确实如此。Safari 果然是现代 IE——至少我是在网上没有找到任何一种方法能够强制 Safari 刷新缓存。

看起来真相终于大白了,最后为了解决这个问题,我只好换成了 Cache-Control: private

本网站所展示的文章由 Zeeko Zhu 采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可

Zeeko's blog, Powered by ASP.NET Core 🐳