摘要
我们都知道了CDN就是以空间换时间,从离用户最近的服务器返回资源给用户,从而达到缩短资源返回时间的目的。这过程中不得不提到缓存,有缓存这个副本存在于各地的服务器上,是上述目的可以完成的前提。针对不一样的业务,不一样的资源,用户可能希望有不一样的缓存行为。例如:哪些资源需要缓存,哪些不需要;经常变动的资源希望缓存时间较短,不常变动的资源缓存时间可以更长;CDN返回资源时希望资源的响应头如何给浏览器设置缓存行为等。本文主要介绍如何配置CDN缓存功能来符合业务场景,以及CDN缓存的工作流程。
CDN缓存概述
使用CDN加速静态资源时,CDN会将源站上的资源缓存到距离客户端最近的CDN节点上。当访问该静态资源时,可直接从CDN的缓存节点上获取,有效避免通过较长的链路回源,提高资源访问效率。

在本文中,将介绍如何通过合理配置CDN缓存功能来提升终端用户访问体验。这里先给出CDN缓存工作流程,可以后面的内容介绍完毕再回过头来看这个图会更好理解。
暂时无法在飞书文档外展示此内容
缓存规则
缓存规则主要用于影响CDN的缓存行为,当收到用户请求时,CDN会根据缓存规则的优先级,由高到低依次将用户请求与缓存规则匹配。在匹配到一条规则后,CDN就会停止匹配其余的规则,以匹配到的规则为准。

如图,缓存规则有一条兜底策略,不允许删除、修改,且这条规则的优先级始终是最低的,用于确保任何文件都可以匹配到一条规则。通过后面的介绍,大家可以再回过头来看,规则匹配到的文件会被如何缓存。
匹配相关配置
- 优先级:表示规则的优先级,数字 1 的规则优先级最高。在收到请求时,CDN按规则的优先级,从高到低尝试将请求与规则匹配。如果请求匹配了某一条规则,CDN就停止匹配其余规则。
- 规则类型:选择一个缓存规则的类型,有三种选择:文件后缀 :缓存规则应用于指定扩展名的所有文件;目录匹配 :缓存规则应用于指定路径下的所有文件;文件全路径:缓存规则应用于那些指定的文件。
- 规则:根据指定的规则类型,设置文件匹配条件。如果规则类型是文件后缀,可以输入一个或者多个文件扩展名如 jpg ;如果规则类型是目录匹配,可以输入一个或者多个目录路径如 /dir ;如果 规则类型 是 文件全路径 ,可以输入一个或者多个文件路径,可以支持作为通配符,例如 **/dir/img/myfile.png**。
缓存相关配置
缓存优先策略 | CDN 关联逻辑 | 缓存行为 | 缓存时长 |
---|---|---|---|
CDN缓存 | 强制缓存(开) | 忽略源站响应头的不缓存指示,始终缓存文件。 | 遵循规则中配置的 过期时间。 |
强制缓存(关) | 不忽略源站响应头的不缓存指示。未指示缓存行为时,不缓存文件。 | ||
遵循源站 | 补充缓存(开) | 遵循源站响应头的指示。未指示缓存行为时,缓存文件。 | 遵循响应头指示的缓存时长。未指示缓存行为时,遵循规则中配置的 过期时间。 |
补充缓存(关) | 遵循源站响应头的指示。未指示缓存行为时,不缓存文件。 | 遵循响应头指示的缓存时长 |
强制缓存(开)
表中列出了不同配置对应的生效行为,可以看到,当设置强制缓存(开),无论源站的缓存行为如何配置,都始终对文件进行缓存。
需要注意的是,当存在以下情况下,无论缓存规则如何设置,都不会对源站响应的文件进行缓存:
- 源站响应中 Content-Length 头的值为 0。
- 用户请求所使用的方法既不是 GET 也不是 HEAD。
- 源站的响应状态码不是 2xx,并且未配置状态码缓存。
- 对于 HEAD 请求,源站响应中包含 Transfer-Encoding:chunked 响应头。
强制缓存(关)
当配置为强制缓存(关),可以理解为遵循源站设置的缓存行为,但缓存时间遵循配置的时间。为了更方便理解,详细来说就是除了下面两种情况,其他情况都会进行缓存:
- 源站响应包含 Cache-Control: no-store 或 Cache-Control: private头部。
- 源站响应不包含 Cache-Control 和 Expires 头部。
补充缓存(开)
当配置为遵循源站(开),可以理解为遵循源站设置的缓存行为,但如果源站没有设置缓存行为时,CDN会缓存文件。且缓存时间也优先遵循源站中的配置,若源站没有指定缓存行为则遵循配置中的缓存时间。详细来说就是除了下面两种情况,其他情况都不会进行缓存:
- 源站响应包含 Cache-Control: max-age 或 Expires 头部。
- 源站响应不包含 Cache-Control 和 Expires 头部。
补充缓存(关)
当配置为遵循源站(开),则一切配置都遵循源站,只有当源站配置了缓存行为,CDN才会对文件进行缓存。也就是只有源站响应包含 Cache-Control: max-age 或 Expires 头部的情况下才会缓存,且缓存时间也永远遵循源站配置。
在以下情况下,除非指定 强制缓存(开) ,CDN都不会缓存指定的文件:
- 源站响应包含 Cache-Control: max-age = 0 头部或者包含的 Expires 头部所指示的时间早于当前时间。
回源校验
当收到用户请求时,CDN根据优先级匹配规则,根据匹配的规则会有以下某个指示:
-
不缓存文件。此时,CDN直接向源站请求该文件。在收到文件后,CDN将该文件响应用户请求,不会缓存该文件。
-
文件需要被缓存。此时,CDN会根据 缓存键值规则 ****检查请求是否命中了缓存。检查结果如下之一:
- 请求命中了缓存,且缓存的文件未过期。此时,CDN将缓存中的文件响应用户请求。
- 请求命中了缓存,但是缓存中的文件已过期。此时,CDN会进行回源 校验。
- 请求未命中缓存。此时,CDN会向源站请求该文件。在收到文件时,CDN将该文件响应用户请求,同时根据该缓存规则的指示缓存该文件。
其他过程都比较清晰,这里主要介绍一下回源校验的流程。当一个缓存文件过期后,如果收到了该文件的请求,CDN会向源站校验该文件是否有更新。回源校验的流程如下:
-
如果该文件的源站响应包含 Last-Modified 字段或者 ETag 头部,CDN会在回源请求中包含用于文件校验的头部。ETag 的优先级高于 Last-Modified。
- 如果源站响应包含 ETag,回源校验请求中会包含 If-None-Match 头部。该头部的值就是 ETag 的值。
- 如果 ETag 不存在但是 Last-Modified 存在,回源校验请求中会包含 If-Modified-Since 头部。该头部的值就是 Last-Modified 的值。
-
如果缓存文件的源站响应中既没有包含 Last-Modified,也没有包含 ETag 头部,内容分发网络就直接向源站请求该文件。在收到文件后,内容分发网络会将文件响应用户请求并缓存该文件。
-
关于源站的响应状态码:
- 如果状态码是 304,表示该文件在源站没有更新。此时,CDN将缓存的文件响应用户请求。
- 如果状态码是 200,表示该文件在源站有更新并且响应正文中包含了最新的文件。此时,CDN会将更新的文件响应用户请求并在缓存中替换已有的文件。
- 无论哪种结果,都会重置过期时间。
一个简单的例子
- 测试源站:demo-test.bytedance.com,端口8888 (这里地址和端口为示例,涉及内部信息不放真实地址和端口)
- 源站文件:demo-test.bytedance.com/ignorecase/smoke.jpg,是一个没有Cache-Control 也没有Expires响应头的文件
- 配置加速域名www.wymtest.com,且源站配置为上述源站
-
当没有配置缓存规则时,访问www.wymtest.com/ignorecase/smoke.jpg,在终端中输入:curl -x xxx.xxx.xxx.xxx:xxxx(这里没有注册dns,需要代理服务器) -sv www.wymtest.com/ignorecase/...
- 得到结果:响应头中没有Cache-Control 也没有Expires,且
x-response-cache: miss
。(图太长了,只截图部分)
- 得到结果:响应头中没有Cache-Control 也没有Expires,且
-
配置缓存规则为如图
- 待配置生效后,再次执行curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
- 这时返回的其他内容都跟第一次一样,不同的是响应头
Age:2
,说明这次请求后,文件被缓存了,且返回的是被缓存的文件,文件缓存时间与文件响应时间之间相差2s。
-
快速再次执行curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
- 这时返回响应头的Age变成了6,当响应返回时,缓存已经存在6s。且x-response-cache返回edge_hit,说明直接命中缓存,并返回缓存。
缓存键值
缓存键是每个在缓存中的文件的唯一标识。在收到一个用户请求时,CDN会对请求生成一个缓存键。默认情况下,缓存键包含 Host、路径、查询参数。例如,www.example.com/image.png?version=1
。然后CDN会尝试将该缓存键与缓存文件的缓存键相匹配,匹配时缓存键是大小写敏感的。
- 如果找到一个匹配,则表示缓存命中,CDN将缓存的文件发送给用户。
- 如果未找到匹配,则表示缓存未命中,CDN会向源站请求该文件。在收到该文件后,CDN会使用该缓存键来缓存文件。
缓存键值配置的目的就是指示CDN在对请求生成缓存键时,缓存键中包含哪些请求 URI 中的查询参数。由于生成的缓存键会被CDN用来匹配缓存的文件,因此缓存键值规则会直接影响缓存命中率。同样,缓存键值也有一条兜底策略。

参数缓存类型
参数缓存类型 | 行为 | 指定参数 |
---|---|---|
保留全部参数 | 包含请求 URI 中全部的查询参数 | 该配置的值必须是 * ,* 表示全部查询参数。 |
删除全部参数 | 不包含任何的查询参数。 | |
删除部分参数 | 包含除了指定参数以外全部的查询参数。 | 可以指定一个或者多个查询参数,但是不能指定 * 。 |
保留部分参数 | 包含指定的查询参数。 |
如果站点上文件 URI 中的某些查询参数并不是用来区分文件的,可以通过创建缓存键规则来指定CDN在生成的缓存键值中需要丢弃的查询参数,以提高缓存命中率,减少回源请求。
站点根目录下的文件 URI 中包含 version,token 这两个参数。这两个参数中,只有 version 是用来区分文件的。例如 /image.png?version=1
与 /image.png?version=2
是两个不同的文件。但是 /image.png?version=1&token=abc
与 /iamge.png?version=1&token=xyz
是相同的文件。CDN先后收到两个该文件的请求。请求的 URI 分别是 /image.png?version=1&token=12345
和 /image.png?version=1&token=abcde
。
如果未配置缓存键值,在收到第一个请求时,内容分发网络缓存该文件。该文件在缓存中的缓存键是 /image.png?version=1&token=12345
。在收到第二个请求时,内容分发网络会使用 /image.png?version=1&token=abcde
去匹配缓存中的文件。由于没有命中缓存,内容分发网络会向源站请求该文件。
思考:如何配置缓存键值可以使得两个请求都命中缓存。
一个简单的例子
仍然用前面的文件来实验,通过前面的配置/ignorecase/smoke.jpg这个文件已经被缓存。
-
执行:curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
- 得到结果:未命中缓存
-
配置缓存键值为如图
- 执行:curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
- 得到结果:命中缓存
状态码缓存
在回源时,如果源站的响应状态码不是 2xx,缓存节点默认不会缓存请求的内容。如果另一个用户请求相同的内容时,还是会触发回源。如果用户希望在短时间内避免这些内容的回源,可以通过配置状态码缓存来降低源站压力。在配置了状态码缓存后,在指定的时间内,如果缓存节点收到了上述情况的内容的访问请求,缓存节点不会回源,而是直接返回状态码。

一个简单的例子
-
我们来访问一个不存在的资源,执行:curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
-
配置状态码缓存如图:
- 这时再次执行,结果与前两次一致,但这次执行已经对该状态码进行了缓存。当第四次执行时,就会发现已经命中缓存。
- 仍然404,但缓存命中。
浏览器缓存
当CDN响应一个用户请求时,除了将请求的文件发送给用户,也可以指示用户的浏览器该如何对该文件进行缓存。通过指定浏览器缓存策略,可以对特定文件在用户浏览器中的缓存行为进行设置。
浏览器缓存策略与源站响应中所指示的缓存行为有关。在向源站请求一个文件时,源站的响应头中会指示该文件的缓存行为,CDN在缓存该文件时会记录该响应头。

缓存策略
策略 | 行为 |
---|---|
缓存 | 如果请求文件的源站响应中 Cache-Control 响应头不包含 no-cache、no-store 或者 private 指令,CDN在响应用户请求时会包含 Cache-Control: max-age=N 响应头。该响应头告知浏览器需要缓存该文件,其缓存时间 N 就是在策略中指定的缓存过期时间。 |
遵循源站 | 在响应用户请求时,CDN会包含请求文件的与缓存相关的源站响应头。 |
不缓存 | CDN指示浏览器不要对请求的文件进行缓存。在响应用户请求时,CDN会包含 Cache-Control: no-store 响应头 |
一个简单的例子
还是用前面的文件,当不配置浏览器缓存时,由于将遵循源站 Cache-Control 响应头的配置,该文件源站没有设置 Cache-Control,所有响应中并没有相关头。
-
执行:curl -x xxx.xxx.xxx.xxx:xxxx -sv www.wymtest.com/ignorecase/...
- 响应中没有缓存相关头。
-
设置浏览器缓存如下
如何提升缓存命中率
CDN缓存命中率包括字节命中率 和请求命中率:
- 字节命中率=CDN缓存命中响应的字节数 ÷ CDN所有请求响应的字节数
- 请求命中率 = CDN缓存命中的请求数÷ CDN所有的请求数
一般我们提到的CDN缓存命中率指的是请求命中率。
命中率间接反应了回源的流量的大小,命中率越低,回源的次数和流量越多,对源站的负载压力也就越大。因此提高CDN的命中率对降低源站访问压力很有必要。
合理配置缓存过期时间
缓存过期时间决定了CDN回源的频率,设置时间越长回源频率越低。
- 更新频率低的文件
可将不常更新的资源文件比如网站的一些图片、模版示例文件、或者一些例如安装包的js、css文件的过期时间设置的长一些,建议设置1个月以上。
- 频繁更新的文件
这类文件可以根据迭代更新频率进行设置,比如如果是两周一迭代,那么可将这类文件的缓存时长设置为14天左右。如果有突发版本低于两周发布的情况,我们也可以使用CDN提供的刷新预热功能进行处理。
预热资源
就是上面提到的刷新预热功能。一般适用于在大型活动发布前,先将发布的内容使用CDN刷新预热功能提前从源站获取资源到CDN缓存节点以提高缓存命中率。这里要注意的是预热功能有一定的限制,所以需要提前规划好资源分配。

备注:为避免刷新、预热配额滥用,每个用户的每日配额有上限限制,需要慎重使用。
配置缓存键值
前面已经详细介绍,通过缓存键值去除站点上文件 URI 中的某些不是用来区分文件的查询参数来提升缓存命中率,此处不再赘述。
大文件设置分片 回源 策略
用户可能只需要大文件的某些分片,也就是说用户的请求是Range请求,或者用户在访问大文件时时可能会中断操作。这两种情况,CDN向源站请求的都是整个完整的资源,这时回源资源内容大于响应给用户的内容,会导致缓存命中率低。
所以针对大文件,建议开启回源Range,开启后CDN在向源站发起请求时就会使用 Range 请求。有关回源Range配置将单独放在「回源配置」另外讲解。
配置共享缓存
当同一个用户的多个加速域名回相同的源站,不同的域名下,同一个url是同一份文件,可以使用一份缓存,所以可以配置域名间的共享缓存,来减少回源。共享缓存即允许多个加速域名共享同一份缓存。

当收到某个共享缓存域名的一个请求,共享缓存的工作流程如下:
- 根据共享缓存域名下的缓存规则,确定请求的文件是否需要被缓存以及缓存的时长。 如果文件不需要被缓存,CDN向共享缓存域名的源站请求该文件,然后使用该文件响应这个请求,流程结束。
- 如果文件需要被缓存,CDN会将请求的文件 URL 中的域名替换成目标域名,然后在缓存中查找请求的文件,如果在缓存中没有找到该文件,则走到第4步。
- 如果在缓存中找到了该文件,CDN会使用步骤1中获取的缓存时长来判断缓存的文件是否已经过期。如果文件没有过期,CDN使用该文件响应这个请求,流程结束。
- 如果文件已经过期或者CDN没有在缓存中找到该文件,则向共享缓存域名的源站请求该文件。
- 从源站获取文件后,将该文件作为目标域名的文件进行缓存,流程结束。