目录

浏览器缓存与CDN缓存详解

浏览器缓存也叫做本地缓存, CDN缓存也可以称为共享缓存.

浏览器缓存tips

浏览器请求一个URL后, 再次请求就默认从本地缓存拿数据了(根据不同的用户行为, 可以有200 from disk304两种状态码), 如果在请求URL加上参数或变化原来的参数, 那么会绕过本地缓存(200 from disk)和协商缓存(304), 直接从CDN缓存服务器获取文件, 返回200状态码, 如果CDN缓存服务器设置的缓存key忽略参数, 并且CDN缓存文件还没有过期, 那么CDN并不会回源.

服务器端不设置缓存策略的情况

服务器端(CDN或源站)如果没有设置客户端缓存时间的策略(即响应头无expirescache-control之类的控制缓存的字段), 不同的浏览器有不同的默认缓存时间.

比如IE浏览器的默认缓存时间就是一个session的时间, 即如果用户打开一个新的IE窗口, 就会从服务器端获取新文件了.

Firefox浏览器的默认缓存时间依赖于last-modified响应头, 缓存时间为文件上一次修改时间距离当前时间的差比10的结果: 默认缓存时间=(当前时间戳-文件上一次修改时间的时间戳)/10.

用户使用ctrl+f5进行强制刷新并不会促使CDN服务器回源获取新文件, 只是跳过浏览器本地缓存和协商缓存, 从CDN缓存服务器获取CDN的缓存文件而已.

只有当CDN的缓存文件过期或者手动清除CDN缓存或者因为LRU策略被CDN服务器自动删除, CDN才会回源获取最新文件.

200 from memory cache:

不访问服务器, 从内存中读取缓存. 此时的数据是缓存到内存中的, 当关闭浏览器后数据将不存在.

200 from disk cache:

不访问服务器, 从磁盘中读取缓存, 当关闭浏览器后数据还是存在.

304 Not Modified:

访问服务器, 发现数据没有更新, 服务器返回此状态码, 然后从本地缓存中读取数据.

如果浏览器请求头中既没有if-modified-since请求头, 又没有if-none-match请求头, 那么CDN缓存文件过期后, CDN将无法304验证回源, 源站直接200响应给CDN.

源站设置过期时间的几种情况

源站的响应头一般都会经过CDN传递给浏览器, 除非在CDN上用新的头覆盖.

  • 源站设置cache-control:max-age=600

    浏览器会缓存文件, 后退前进、新打开窗口在地址栏打开链接均为200 from disk, 不会从服务端验证(304), 除非本地缓存过期, 或者F5、ctrl+f5.

    CDN会缓存文件.

  • 当源站设置5m过期, 生效后马上又变更为10m

    浏览器缓存将一直按照5m, 即使CDN缓存文件过期(文件内容不变的情况下)也是按照5m, 除非将CDN原有缓存文件删除;

    CDN缓存先是按5m, 过期后将按照10m.

  • 源站设置cache-control:max-age=0

    浏览器会缓存文件, 后退或前进都为200 from disk, 新打开窗口在地址栏打开链接, 先要去服务端协商验证文件是否已更新(304).

    CDN会缓存文件, 但是每次请求过来都会去源站验证文件是否过期, 若未过期, 则源站返回304.

  • 如果源站设置cache-control:no-cache时, 如果源是Nginx, 即expires -1

    浏览器会缓存文件, 后退前进200 from disk, 新打开窗口在地址栏打开链接, 先要去服务端协商验证文件是否已更新(304).

    CDN将不缓存.

  • 源站的Nginx设置expires epoch

    浏览器会缓存, 后退前进可以看到200 from disk, 但是新打开窗口在地址栏打开链接则不验证缓存, 直接从服务端获取新数据, 服务端返回200.

    CDN不缓存.

  • 源站设置cache-control:no-store

    浏览器不会缓存, 后退、前进、新窗口打开等操作均要向服务端获取新数据, 服务端均返回200.

    CDN不缓存.

  • 源站设置cache-control:max-age=10,s-maxage=1000;

    表示设置CDN缓存1000秒, 浏览器缓存10秒.

    浏览器将缓存10s, s-maxage对浏览器无效;

    如果源站只设置s-maxage, 那么CDN将按照这个缓存, 浏览器将按照自身默认缓存.

    CDN缓存1000s, s-maxage优先与max-age.

CDN缓存tips

CDN上缓存文件的过期时间设置的3种情况

  • 源站配置缓存时间

  • 用户在CDN控制台进行缓存策略的具体配置

  • CDN有自己的默认缓存时间策略:

    一般只有在用户既没有在源站上配置缓存时间, 又没有在CDN控制台配置的时候, CDN默认缓存策略才会生效, 不同的厂商有不同的策略.

    拿阿里云CDN举例, 默认缓存时间是当前时间减去源站响应的last-modified头时间再乘以0.1, 有取值限制, 最大值3600秒, 最小值10秒, 如果源站没有响应last-modified头, 但是有etag头, 则将默认缓存时间设置为10秒, 如果last-modifiedetag头都没有, 则认为是动态内容, 将默认缓存时间设置为0, 即不缓存, 每次都回源.

以上3中情况, CDN一般遵循的优先级为2>1>3.

如果CDN变更缓存时间策略, 对于已经缓存的文件来说没有效果, 只有当已缓存文件过期或者手动清理已缓存文件后, 新的缓存时间策略才会对这些文件生效.

如果CDN没有该缓存文件, 则回源获取, 源站返回给CDN200状态码, CDN表现为MISS, 返回浏览器200304.

如果CDN有该缓存文件并且没有过期, CDN表现为HIT, 返回浏览器200304.

如果CDN缓存文件过期, 则回源获取, CDN表现为EXPIRED. CDN回源的请求头会带上if-modified-since头:

  • 当源站该文件没有变更, 则源站返回304状态码给CDN服务器, 此时CDN服务器会继续使用CDN原有缓存文件并把该缓存文件过期时间增加为源站配置的秒数或者CDN配置的秒数, 源站优先. CDN返回给浏览器304200.
  • 当源站该文件已经更新, 则源站返回200状态码给CDN服务器, CDN更新缓存文件, 返回给浏览器200.

以上CDN返回给浏览器的状态码解读:

  • 200: (ctrl+f5强制刷新、浏览器本地没有缓存、浏览器本地缓存过期并且到CDN进行对比后发现文件已经更新)
  • 304: (f5刷新、浏览器本地缓存过期并且到CDN进行对比后发现文件没有更新)

关于缓存刷新

  • URL刷新

    是指强制将CDN缓存的某个文件设置为已过期, 这样浏览器再请求过来的时候CDN就会回源去验证文件是否更新了, 更新了则取过来, 没更新则继续使用CDN原有的缓存文件并行使原有的缓存时间策略. 刷新缓存并不是删除缓存, 只是让CDN下次响应用户之前要先去源站验证文件是否为最新的.

  • 目录刷新

    是指强制将CDN缓存的某个目录下的文件均设置为过期, 本质和URL刷新是一致的, 目录刷新可以理解为批量做URL刷新.

缓存服务器行为与浏览器行为示意图

../../imgs/cdn-browser-cache.png

注意: 本图是在阿里云官方文档的原图基础上改进的, 我觉得阿里云的原图貌似有疏漏之处, 故重新画了一个. 阿里云原图如下:

https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/27136/cn_zh/1463971630525/cache.png