CDN其实是一个缓存系统,那么这个缓存系统的key是跟什么相关呢?首先想到的就是URL,以及可以控制是否考虑URL上问号后面的参数,但有些时候这样是不够的,特别有如下两个场景:
1. 安卓、chrome(Google他们家的)都是支持webp的,而众所周知,苹果家的safari是不支持的。那么对于同样的请求,就需要根据不同的客户端返回不同格式的图片用于展示;
2. 各方面来看br是更适合于网络资源压缩的压缩算法,而老一些的客户端还是只支持gzip。那么对于同样的请求,就需要根据不同的客户端返回不同的压缩格式的数据;
针对这种,同样的请求,会有不同版本的缓存数据的情况,可以将各家CDN服务商分成三类:
1. 支持直接在后台设置缓存key需要考虑哪些http header在内,如:阿里云、aws
2. 不支持直接配置,但是支持http的Vary头来控制,这就需要有一定的后端能力了,如:百度云
3. 只能以url及参数作为缓存key,简单说就不支持这种优化需求,比如想要使用webp,就需要客户端自己做好检测来决定应该请求哪个url,如:七牛云
当然阿里云和aws也是CDN费用最贵的一档了,关于如何配置缓存key,可移步《阿里云和aws的CDN回源策略的缓存key配置》。
这里说一下Vary对CDN缓存的控制,http返回的Vary header就是告诉CDN,如果哪个header发生变化了,那么这个返回结果可能就不对了。
对于上面的场景1,我们需要后端返回时带上Vary: accept
,对于上面的场景2,后端返回时需要带上Vary: accept-encoding
。
很多时候,我们是通过nginx来直接服务于静态资源的访问,这时不涉及后端代码,那该如何配置nginx呢?重点如下:
gzip on;
gzip_vary on;
gzip_proxied any; #ignore Via header
...
brotli on;
...
gzip_vary就是用于添加Vary: accept-encoding头,这里的gzip_proxied需要额外注意,因为CDN是代理缓存,所以请求会带Via头过来,而nginx认为代理缓存不应该压缩,所以如果有Via存在,就算请求头里明示了支持gzip,也是不会进行压缩的。所以需要设置gzip_proxied。
brotli是谷歌提供的nginx扩展,默认就会带上Vary,也默认就会忽略掉Via,配置起来比较简单。