CDN:内容分发网络与静态资源加速

CDN 是什么

CDN,全称是 Content Delivery Network,中文通常叫内容分发网络。

它不是某一台服务器,而是一套分布在不同地域、不同运营商网络中的边缘节点集群。它的核心目标是:把用户要访问的资源提前缓存或分发到离用户更近、网络质量更好的节点上,让用户不必每次都跨地域、跨运营商访问源站。

可以把 CDN 理解成“离用户更近的资源中转站”:

没有 CDN:

用户 ───────────────> 源站服务器
      距离远、链路长、源站压力大

使用 CDN:

用户 ──> 就近 CDN 节点 ──> 源站服务器
      多数请求命中缓存       只有未命中或过期时回源

在前端项目中,CDN 最常用于加速静态资源,例如 JavaScript、CSS、图片、字体、音视频、下载文件等。对于动态接口,CDN 也可以做边缘缓存、接口加速、安全防护和请求转发,但是否适合缓存,需要看业务数据是否允许被多个用户共享。

CDN 解决了什么问题

降低访问延迟

如果一个中国用户访问部署在美国的源站,网络请求可能要经过较长的国际链路。使用 CDN 后,用户可能访问的是本地或邻近城市的边缘节点,TCP/TLS 建连时间和数据传输时间都会明显下降。

减轻源站压力

大量静态资源请求会被 CDN 节点直接响应,源站只需要处理缓存未命中、资源过期校验、动态请求或管理类请求。对于大促、活动页、图片站、下载站等场景,这一点非常关键。

提高可用性

CDN 厂商通常拥有多节点、多线路、多运营商调度能力。某个节点异常时,可以把流量调度到其他节点;对于已经缓存在边缘节点上的资源,请求会直接由 CDN 返回,减少对源站可用性和瞬时负载的依赖。

改善跨运营商访问

不同运营商之间的互联质量可能不稳定。所谓跨运营商访问,指的是用户所在网络和资源所在服务器不在同一个运营商网络里。例如用户使用中国移动宽带,但源站部署在中国电信机房,请求就可能经过移动网络、电信网络以及两者之间的互联出口:

移动用户


移动骨干网


移动 / 电信互联链路


电信骨干网


电信机房源站

跨运营商链路在高峰期可能出现延迟升高、丢包、拥塞和下载速度不稳定。CDN 的做法是在多个运营商网络中部署边缘节点,并通过调度系统尽量让用户访问同运营商或链路质量更好的节点:

广州移动用户 ──> 华南移动 CDN 节点
北京联通用户 ──> 华北联通 CDN 节点
上海电信用户 ──> 华东电信 CDN 节点

如果 CDN 节点缓存命中,请求会直接在边缘节点返回,不需要跨运营商访问源站。即使缓存未命中,需要由 CDN 节点回源,跨运营商或跨地域链路也主要发生在 CDN 到源站之间,而不是每个用户都直接承受这条链路。

提供安全与流量治理能力

实际 CDN 服务通常还会集成 HTTPS 证书托管、访问控制、防盗链、限速、WAF、DDoS 防护、日志分析、边缘规则、图片处理等能力。严格来说,这些不是 CDN 的最基础定义,但在真实项目里经常与 CDN 一起使用。

CDN 的核心组成

源站

源站是资源的原始服务器,可以是业务服务器、对象存储、静态资源服务器或负载均衡入口。CDN 节点没有资源或缓存过期时,会向源站请求资源,这个过程叫回源。

例如:

源站地址:origin.example.com
CDN 域名:static.example.com
资源路径:/assets/app.8f3a1c.js

用户访问 static.example.com/assets/app.8f3a1c.js,CDN 节点如果没有缓存,就会去源站请求 /assets/app.8f3a1c.js

边缘节点

边缘节点是离用户更近的 CDN 缓存服务器。它负责接收用户请求,判断本地是否有可用缓存:

  • 命中缓存:直接返回资源。
  • 未命中缓存:向上层 CDN 节点或源站请求资源,拿到响应后按策略缓存,再返回给用户。
  • 缓存过期:根据缓存策略重新验证或重新拉取资源。

用户请求 CDN 资源时,DNS 最终返回的通常不是源站 IP,而是某个 CDN 边缘节点、节点集群或负载均衡入口的 IP:

static.example.com
  CNAME static.example.com.cdnvendor.net
  A     203.0.113.10

这里的 203.0.113.10 就可以理解成 CDN 边缘节点 IP。浏览器拿到这个 IP 后,会和这个 CDN 节点建立 TCP/TLS 连接,再发送 HTTP 请求。

但“CDN 节点”通常不是单台服务器,而是某个区域或运营商网络中的边缘服务集群。DNS 返回的节点 IP 可能是某台缓存服务器的 IP,也可能是负载均衡入口、VIP、Anycast IP,或多台缓存服务器共同对外服务的一组 IP。请求进入节点后,节点内部还可能继续做负载均衡,把请求分配给具体缓存服务器处理。

用户


DNS 返回边缘节点 IP


CDN 节点入口


节点内部负载均衡


具体缓存服务器处理请求

一般业务公司不需要自己购买这些节点。公司购买的是 CDN 服务,边缘节点、机房、带宽、IP、缓存集群和调度系统由 CDN 厂商建设和维护。业务方需要配置加速域名、源站、缓存规则、证书和安全策略。

调度系统

调度系统决定用户应该访问哪个 CDN 节点。常见调度方式包括 DNS 调度、HTTPDNS、Anycast IP、302 调度等。前端面试中最常见的是 DNS 调度。

DNS 调度中有一个重要概念:调度域名。它通常是 CDN 厂商给业务方的 CNAME 目标,用来把某个业务加速域名的后续 DNS 解析引到 CDN 厂商。

static.example.com CNAME static.example.com.cdnvendor.net

static.example.com 是用户访问的业务加速域名,static.example.com.cdnvendor.net 是 CDN 厂商提供的调度域名。调度域名本身不存放资源,它的作用是把 DNS 查询引到 CDN 厂商控制的解析体系。

当递归 DNS 继续查询 static.example.com.cdnvendor.net 时,请求会到达 CDN 厂商的权威 DNS。这个权威 DNS 负责对 CDN 厂商自己的域名区域给出最终解析结果;它通常会结合背后的调度系统,根据用户大致位置、运营商、节点健康、节点负载和线路质量等因素,返回一个或多个合适的边缘节点 IP。

所以,CDN 厂商的权威 DNS 是 DNS 查询入口和响应出口,调度系统是背后的节点选择逻辑。二者配合完成 DNS 调度,但不能简单理解成同一个东西。

业务方通常不会直接把加速域名配置成 CDN 节点 IP 的 A 记录。因为 CDN 节点数量很多,而且会随地域、运营商、节点健康、节点负载、容量调整和故障切换动态变化。如果业务方直接维护这些 IP,不仅维护成本高,也会失去 CDN 厂商按实时网络质量做调度的能力。使用 CNAME 指向 CDN 调度域名,本质上是把“具体返回哪个边缘节点 IP”这件事交给 CDN 厂商处理。

缓存系统

缓存系统负责保存资源副本,并根据 HTTP 缓存头、CDN 控制台规则、缓存键、刷新预热策略决定资源是否可缓存、缓存多久、什么时候回源。

它不是简单地“按 URL 存文件”。真实 CDN 会先判断响应是否允许被共享缓存,例如是否有 Cache-Control: public,是否包含 privateno-storeSet-CookieAuthorization 等敏感信号;然后根据协议、域名、路径、查询参数,以及部分请求头或 Cookie 生成缓存键。

当请求到达边缘节点时,缓存系统会判断资源是命中、未命中还是已过期:命中时直接返回;未命中时回源拉取;过期时可能重新回源拉取,也可能通过 ETagLast-Modified 与源站做协商校验。缓存系统设计不好,可能导致命中率低、回源压力大、旧资源迟迟不更新,甚至把用户私有数据错误缓存给其他用户。

回源链路

回源链路是 CDN 节点到源站之间的请求路径。为了减少源站压力,CDN 可能存在多级缓存:

用户


边缘节点
  │ 未命中

区域父节点
  │ 未命中

源站

多级缓存可以避免大量边缘节点同时打到源站。

一次 CDN 请求的完整链路

以页面引用一个静态脚本为例:

<script src="https://static.example.com/assets/app.8f3a1c.js"></script>

用户第一次请求该资源时,链路通常如下:

1. 浏览器发现需要加载 app.8f3a1c.js
2. 浏览器查询 static.example.com 的 DNS
3. DNS 根据 CNAME 进入 CDN 调度系统
4. CDN 调度系统返回合适的边缘节点 IP
5. 浏览器与该 CDN 节点建立 TCP 连接
6. 如果是 HTTPS,浏览器与 CDN 节点进行 TLS 握手
7. 浏览器发送 HTTP 请求
8. CDN 节点根据缓存键查找本地缓存
9. 如果命中缓存,CDN 直接返回资源
10. 如果未命中缓存,CDN 节点向源站回源
11. 源站返回资源和缓存头
12. CDN 节点按策略保存资源副本
13. CDN 节点把资源返回给浏览器
14. 浏览器解析、执行或渲染该资源

可以画成更直观的形式:

浏览器

  │ 1. DNS 查询 static.example.com

本地 DNS / 递归 DNS

  │ 2. 根据 CNAME 进入 CDN 调度

CDN DNS / 调度系统

  │ 3. 返回边缘节点 IP

浏览器

  │ 4. TCP + TLS + HTTP 请求

CDN 边缘节点

  ├─ 5a. 缓存命中:直接返回资源

  └─ 5b. 缓存未命中:回源拉取资源


              源站

第二个用户或同一用户再次请求时,如果命中 CDN 缓存,链路会短很多:

浏览器 ──DNS/连接──> CDN 边缘节点 ──直接返回缓存资源──> 浏览器

用户访问的是 CDN 节点,但 URL 中的域名仍然是业务域名 static.example.com。HTTPS 证书也需要覆盖这个域名,TLS 握手通常发生在浏览器和 CDN 节点之间。CDN 再回源时,可以使用 HTTP 或 HTTPS,真实项目中更推荐 HTTPS 回源。

DNS 调度与 CNAME

假设业务配置如下:

static.example.com CNAME static.example.com.cdnvendor.net

这里的 static.example.com.cdnvendor.net 不是资源服务器地址,不表示这个域名背后有一台机器专门存放 app.jslogo.png。它更像一个 DNS 调度入口:递归 DNS 查询到这个域名时,会进入 CDN 厂商控制的权威 DNS。

static.example.com 属于业务域名 example.com,它的 CNAME 记录由 example.com 的权威 DNS 返回;static.example.com.cdnvendor.net 属于 CDN 厂商的域名 cdnvendor.net,所以最终解析它的是 CDN 厂商管理的权威 DNS。普通权威 DNS 往往返回预先配置好的记录,而 CDN 厂商的权威 DNS 背后接了调度系统,可以根据用户位置、运营商、节点健康、节点负载和线路质量,动态返回更合适的边缘节点 IP。

用户解析 static.example.com 时,大致过程是:

  1. 浏览器、操作系统、路由器或本地 DNS 先查缓存。
  2. 没有缓存时,递归 DNS 按 DNS 层级查询。
  3. example.com 的权威 DNS 返回 static.example.com 的 CNAME。
  4. 递归 DNS 继续解析 CDN 厂商的调度域名。
  5. CDN 厂商的权威 DNS 根据递归 DNS 出口、EDNS Client Subnet、节点状态、负载和网络质量等信息返回 CDN 节点 IP。
  6. 浏览器拿到 IP 后,与该节点建连并发送请求。

完整过程可以这样理解:

用户访问 static.example.com


递归 DNS 查询 static.example.com


example.com 的权威 DNS 返回:
static.example.com CNAME static.example.com.cdnvendor.net


递归 DNS 继续查询 static.example.com.cdnvendor.net


cdnvendor.net 的权威 DNS 返回:
static.example.com.cdnvendor.net A 203.0.113.10

这条链路遵循的是标准 DNS 协议:域名分层、递归查询、迭代查询、CNAME 继续解析、A / AAAA 返回地址、TTL 缓存等规则都是统一的。否则不同 DNS 服务商和客户端之间就无法互通。

不完全统一的是权威 DNS 收到查询后的返回策略。普通 DNS 服务可能只是返回预先配置好的固定记录,例如 www.example.com A 1.2.3.4;而 CDN 厂商的权威 DNS 背后通常接了调度系统,可以根据递归 DNS 来源 IP、EDNS Client Subnet、用户大致地域、运营商、节点健康、节点负载、网络质量、业务配置和成本等因素,动态返回不同边缘节点 IP。

所以,DNS 的协议规则是统一的;CDN 厂商的智能解析和调度策略可以不同。

这里要注意:CNAME 通常配置在 example.com 这个 DNS 区域的权威 DNS 中,而不是配置在一个单独的“static.example.com 的权威 DNS”中。因为 static.example.com 多数情况下只是 example.com 区域下的一条记录。

例如你购买并托管了 example.com,DNS 服务商负责 example.com 区域,那么可以在这个区域里配置:

static.example.com CNAME static.example.com.cdnvendor.net

也可以写成区域内的相对形式:

static CNAME static.example.com.cdnvendor.net.

只有当你把 static.example.com 单独委派成一个子区域时,才会有所谓“static.example.com 的权威 DNS”。例如在 example.com 区域里配置:

static.example.com NS ns1.other-dns.com
static.example.com NS ns2.other-dns.com

这时 static.example.com 才成为独立 zone,后续 a.static.example.comcdn.static.example.com 之类记录才由它自己的权威 DNS 管。实际 CDN 接入里通常只是给一个加速域名加一条 CNAME,不需要把它单独委派出去。

CNAME 本身不负责“选择节点”,它只负责把解析流程引到 CDN:

业务加速域名

  │ CNAME

CDN 调度域名

  │ CDN 权威 DNS 动态解析

边缘节点 IP

CDN 权威 DNS 不会固定返回同一个 IP,而是根据调度规则选择当前更适合这个用户访问的节点。

调度规则作用
地理位置优先选择距离用户较近的区域节点,减少长距离传输
运营商线路电信用户优先电信线路节点,联通用户优先联通线路节点,减少跨网访问
递归 DNS 出口传统 DNS 下,CDN 常根据递归 DNS 的来源 IP 判断用户大致位置
EDNS Client Subnet如果 DNS 查询携带用户 IP 网段,CDN 可以更准确判断终端用户位置
节点健康状态故障、不可用或探测异常的节点不返回,或者降低权重
节点负载带宽、连接数、CPU、缓存压力较高的节点少返回或不返回
网络质量根据延迟、丢包、拥塞、跨运营商质量等实时或准实时数据选择节点
节点容量和成本在体验可接受的前提下,结合带宽成本、容量水位做调度
缓存分布某些系统会考虑资源是否已在某个节点或上层节点存在
业务配置按域名、区域、客户等级、灰度策略、灾备策略、合规要求选择节点池

一个简化的调度流程可以这样理解:

CDN 权威 DNS 收到查询


识别请求来源:递归 DNS IP / EDNS Client Subnet


判断用户大致地域和运营商


筛选该业务可用的节点池


排除故障节点和不可服务节点


根据负载、网络质量、成本、缓存情况排序


返回一个或多个边缘节点 IP

传统 DNS 调度有一个限制:权威 DNS 通常看到的是递归 DNS 的地址,不一定能准确看到终端用户的真实地址。因此,如果用户使用了跨地区的公共 DNS,调度结果可能不够精准。EDNS Client Subnet 可以在一定程度上改善这个问题,但也会带来隐私和兼容性权衡。

这一步的目标不是“绝对最近”,而是“综合网络质量最合适”。物理距离近不一定代表访问质量最好。例如北京用户不一定永远拿到北京节点。如果北京节点故障、负载很高,或者北京节点与该用户运营商之间的链路质量不好,CDN 可能返回天津、河北、山东甚至其他区域的节点。

DNS 响应也可能返回多个边缘节点 IP:

static.example.com.cdnvendor.net A 203.0.113.10
static.example.com.cdnvendor.net A 203.0.113.11
static.example.com.cdnvendor.net A 203.0.113.12

多个 IP 通常用于负载均衡和容灾。递归 DNS 会把这些 IP 返回给客户端,之后由浏览器、操作系统或网络协议栈选择其中一个建立连接。客户端可能选择第一个 IP,也可能根据本地策略调整顺序;如果同时存在 IPv4 和 IPv6,还可能通过 Happy Eyeballs 机制并行或错峰尝试。

如果某个 IP 连接失败或超时,客户端可能继续尝试下一个 IP。建连成功后,浏览器通常会在连接可复用期间继续使用这个连接。也就是说,DNS 调度负责返回一组相对合适的候选地址,真正连接哪个 IP,是客户端在发起连接时完成的最后一步选择。

CDN 缓存策略

CDN 缓存和浏览器缓存都能减少重复请求,但它们位于不同位置,解决的问题也不同。

对比项浏览器缓存CDN 缓存
缓存位置用户本机浏览器CDN 边缘节点
共享范围通常只服务当前用户可服务同一节点覆盖范围内的多个用户
主要目标减少本机重复下载减少跨地域传输和源站压力
控制方式HTTP 缓存头、浏览器策略HTTP 缓存头、CDN 控制台规则、刷新预热
未命中后访问谁直接访问目标服务器或 CDN访问源站或上层 CDN 节点

一次请求可能同时经过两层缓存:

浏览器缓存 → CDN 缓存 → 源站

浏览器本地缓存命中时,请求甚至不会发到 CDN;浏览器缓存过期但 CDN 缓存命中时,请求会到 CDN,但不会到源站;两者都未命中时,才会回源。

用户请求到达 CDN 节点后,节点会根据请求 URL、请求头和缓存配置生成缓存键,然后判断是否存在可用缓存。

情况CDN 行为
缓存命中且未过期CDN 直接返回资源,不访问源站
缓存不存在CDN 向源站请求资源,成功后缓存并返回
缓存已过期CDN 按策略重新验证或重新拉取资源
资源被配置为不缓存CDN 透传请求到源站,或只做链路加速
源站异常CDN 可能返回错误,也可能返回过期缓存,取决于配置

缓存是否生效,通常由源站响应头和 CDN 配置共同决定。

静态构建资源

对于带内容哈希的 JS、CSS、图片等构建资源,常见策略是长期缓存:

Cache-Control: public, max-age=31536000, immutable

原因是文件内容变化后文件名会变化,旧文件可以长期缓存,新页面会引用新文件。

HTML 入口文件

HTML 可以经过 CDN,但通常不适合长期强缓存。原因是 HTML 是资源入口,它决定当前页面引用哪些 JS、CSS 和图片。发布新版本时,HTML 里的资源引用会变化:

<!-- 旧 HTML -->
<script src="/assets/app.a1b2c3.js"></script>

<!-- 新 HTML -->
<script src="/assets/app.d4e5f6.js"></script>

如果 HTML 被长期强缓存,用户可能一直拿到旧 HTML,从而继续加载旧 JS。因此,SPA 的 index.html 最常见策略是:

Cache-Control: no-cache

no-cache 不是完全不缓存,而是表示可以缓存,但使用缓存前必须向服务器或 CDN 验证是否仍然最新。如果内容没有变化,服务端可以返回 304 Not Modified,浏览器继续使用本地缓存;如果内容变化,则返回新的 HTML。

常见 HTML 缓存策略如下:

场景推荐策略说明
SPA 入口 index.htmlCache-Control: no-cache最常见,确保用户能尽快拿到最新 JS/CSS 引用
普通公开页面Cache-Control: public, max-age=60允许短时间缓存,降低源站压力
新闻、商品详情等允许延迟页面Cache-Control: public, max-age=60, s-maxage=300浏览器短缓存,CDN 等共享缓存稍长缓存
静态落地页、活动页短缓存 + 发布时刷新 CDN兼顾访问性能和内容更新
登录态或个性化 HTMLCache-Control: private, no-cache只允许浏览器私有缓存,不应被公共 CDN 共享缓存
支付页、强实时或敏感页面Cache-Control: no-store不存储缓存,每次重新获取

其中 s-maxage 主要给 CDN 这类共享缓存使用,优先级通常高于 max-ageprivate 表示响应只应被浏览器这类私有缓存保存,不应被 CDN 共享缓存保存。

动态接口:公共接口可以缓存

CDN 不只会缓存 JS、CSS、图片这类静态文件,也可以缓存动态接口的响应。关键不在于“接口是不是动态生成的”,而在于这个响应是否适合被多个用户共享,以及是否允许一定的数据延迟。

公开且允许短暂延迟的接口可以设置较短缓存:

Cache-Control: public, max-age=60, s-maxage=300

适合缓存的接口通常具有这些特点:

  • 响应对多个用户相同。
  • 数据允许短时间延迟。
  • 可通过 URL、请求头或查询参数明确区分版本和维度。
  • 不包含用户隐私、权限、登录态数据。

例如文章详情、商品基础信息、公开配置、城市列表、公开排行榜等,虽然响应可能是后端动态生成的 JSON,但如果结果对多个用户相同,就可以设置短 TTL 或使用边缘缓存。

动态接口:私有接口不能公共缓存

登录态接口、订单信息、用户资料、购物车、账户余额等私有数据一般不应被公共 CDN 缓存。因为这些响应和用户身份相关,如果被 CDN 当成公共缓存,可能出现严重的缓存串数据问题:A 用户的订单被缓存后,B 用户请求同一 URL 时拿到了 A 的订单。

涉及用户隐私、账户、订单、权限的数据,应避免被共享缓存:

Cache-Control: private, no-store

或者至少使用:

Cache-Control: private, no-cache

不要让带 Set-CookieAuthorization 或用户身份信息的响应被公共 CDN 错误缓存。

如果身份相关接口确实要经过 CDN,通常也是为了链路加速、TLS 终止、WAF、DDoS 防护、访问控制、边缘鉴权或请求转发,而不是让它进入公共缓存。除非使用了严格的缓存键、私有缓存、鉴权和隔离策略,否则不要缓存这类响应。

缓存键

CDN 判断“两个请求是不是同一个缓存资源”,靠的是缓存键。默认情况下,缓存键通常包括协议、域名、路径和查询参数,也可能包括部分请求头。

例如:

https://static.example.com/logo.png
https://static.example.com/logo.png?version=1
https://static.example.com/logo.png?version=2

如果 CDN 把完整查询参数纳入缓存键,这三个请求会被视为不同资源。真实项目里要特别关注:

  • 是否忽略某些无意义参数,例如埋点参数 utm_source
  • 是否保留影响内容的参数,例如 width=400format=webp
  • 是否按请求头区分缓存,例如 Accept-EncodingAccept-Language
  • 是否按 Cookie 或 Authorization 区分缓存。

缓存键设计错误会导致两类问题:命中率低,或者更严重的缓存串数据。

发布与缓存更新

CDN 缓存更新主要有四种方式:自然过期、主动刷新、预热和版本化发布。实际项目里通常组合使用。

自然过期

自然过期是最基础的更新方式。CDN 会根据源站响应头或 CDN 控制台规则决定资源缓存多久。

Cache-Control: public, max-age=86400

表示资源可以缓存 86400 秒。缓存有效期内,CDN 命中后会直接返回资源;缓存过期后,CDN 会重新回源校验或拉取新资源。

第一次请求:CDN 未命中 → 回源拉取 → 缓存资源
后续请求:CDN 命中 → 直接返回
缓存过期:CDN 重新回源校验或拉取新资源

自然过期的优点是简单,缺点是缓存没过期前用户可能一直拿到旧资源。因此,URL 不变但内容会更新的资源,不应只依赖很长的 TTL。

刷新

刷新是告诉 CDN 删除或标记某些缓存失效。用户下一次访问时,CDN 会重新回源拉取。

常见刷新方式:

  • 按 URL 刷新:精确刷新某个文件。
  • 按目录刷新:刷新某个路径下的资源。
  • 按正则或规则刷新:部分 CDN 支持。
  • 全站刷新:清空整个加速域名下的缓存,通常不建议频繁使用。

刷新适合修复错误资源、撤回内容、更新非哈希文件。但刷新不是越多越好,大量刷新会造成回源峰值。刷新通常也不是全网瞬间完成的,可能需要几十秒到几分钟传播,具体取决于 CDN 厂商、节点数量和刷新规模。

预热

预热是提前让 CDN 节点去源站拉取资源。这样用户第一次访问时也能尽量命中 CDN 缓存。

预热适合:

  • 大促活动页资源。
  • 大文件下载。
  • 新版本发布后的核心静态资源。
  • 热门图片、视频封面等高频资源。

刷新是“让旧缓存失效”,预热是“提前准备新缓存”。两者经常配合使用:先刷新错误或旧资源,再预热新资源,避免用户首次访问时集中回源。

版本化发布

前端 JS、CSS 和构建产物最推荐使用版本化发布,也就是文件名携带内容哈希,而不是覆盖旧文件。

旧版本:

/assets/app.a1b2c3.js

新版本:

/assets/app.d4e5f6.js

HTML 更新为引用新文件:

<script src="https://static.example.com/assets/app.d4e5f6.js"></script>

这样旧文件即使仍然缓存在 CDN 中也没关系,因为新页面请求的是新 URL。CDN 会把不同 URL 视为不同缓存对象:

/assets/app.a1b2c3.js  → 旧缓存
/assets/app.d4e5f6.js  → 新资源,首次请求时回源拉取

推荐策略是:

// HTML 入口
Cache-Control: no-cache

// 带内容哈希的 JS / CSS / 图片
Cache-Control: public, max-age=31536000, immutable

这样做可以同时保证发布及时性和静态资源缓存收益:HTML 用短缓存或协商缓存,确保用户能拿到最新资源引用;带哈希的 JS、CSS、图片使用长缓存,减少重复下载。

旧 hash 资源如何删除

文件 hash 变化后,CDN 通常不会在新版本发布时立刻删除旧资源。因为旧资源和新资源是两个不同 URL,CDN 会把它们当成两个不同的缓存对象。新 HTML 引用新文件后,新用户会请求 /assets/app.d4e5f6.js;但仍然可能有用户拿着旧 HTML,继续请求 /assets/app.a1b2c3.js

旧 hash 资源通常通过以下方式逐渐消失:

清理方式说明
TTL 到期旧缓存达到过期时间后,CDN 会重新校验、回源或标记失效
缓存淘汰CDN 节点存储有限,长期无人访问的冷门资源可能被淘汰
主动刷新如果确定旧资源不应再访问,可以刷新旧 URL,让 CDN 删除或标记失效
源站清理源站或对象存储定期删除旧版本文件,例如只保留最近几版或保留 30 天

前端 hash 静态资源一般不需要发布后立刻主动刷新旧 URL。更推荐让旧资源保留一段时间,原因是:用户可能仍然持有旧 HTML,如果源站太早删除旧 JS,而某个 CDN 节点又没有缓存这个旧 JS,CDN 回源会得到 404,页面可能加载失败甚至白屏。

更稳妥的发布策略是:

1. 先上传新资源 app.d4e5f6.js
2. 再更新 HTML,让它引用新资源
3. HTML 使用短缓存或 no-cache,尽快让用户拿到新引用
4. 旧 HTML 用户仍可继续请求旧资源 app.a1b2c3.js
5. 等一个安全窗口后,源站再清理旧版本资源
6. CDN 旧缓存通过 TTL 到期或缓存淘汰逐渐消失

因此,hash 文件名的价值不只是“方便强缓存”,还在于允许新旧资源并存,避免发布瞬间强制清理缓存带来的不可用风险。

CDN 与 HTTPS 和安全

使用 HTTPS 的 CDN 链路通常分成两段:

浏览器 ──HTTPS──> CDN 节点 ──HTTPS 或 HTTP──> 源站

第一段是客户端到 CDN。用户访问的是:

https://static.example.com/assets/app.js

虽然 DNS 最终返回的是 CDN 边缘节点 IP,但 URL 里的域名仍然是 static.example.com。因此 TLS 握手时,CDN 节点必须返回匹配 static.example.com 的证书。浏览器验证通过后,才会继续发送 HTTP 请求。

第二段是 CDN 到源站,叫回源协议。可以配置 HTTP 回源或 HTTPS 回源。为了避免外部链路被窃听或篡改,生产环境更推荐 HTTPS 回源。

两类证书的区别如下:

对比项CDN 侧证书源站证书
部署位置CDN 边缘节点源站服务器、源站负载均衡或对象存储服务
服务链路浏览器 → CDNCDN → 源站
主要验证方浏览器CDN 节点
匹配域名用户访问的加速域名,例如 static.example.com源站域名或回源 Host,例如 origin.example.com
是否必须用户通过 HTTPS 访问 CDN 时必须HTTPS 回源时必须,HTTP 回源时不需要
管理方式CDN 控制台申请、上传、续期和部署源站服务器、负载均衡、对象存储或云证书服务管理

如果配置为:

浏览器 ──HTTPS──> CDN 节点 ──HTTP──> 源站

用户地址栏仍然显示 HTTPS,但 CDN 到源站这一段不是加密链路。这种方式配置简单,但不属于端到端加密,生产环境通常更推荐 HTTPS 回源。

这里还要注意:如果 CDN 终止 TLS,那么 CDN 节点可以看到明文 HTTP 内容,然后再决定是否缓存、压缩、改写或回源。因此涉及高度敏感的数据时,需要谨慎评估 CDN 的角色、配置和合规要求。

CDN 常见安全配置包括:

  • HTTPS 证书和 TLS 配置。
  • HTTP 自动跳转 HTTPS。
  • Referer 防盗链。
  • Token 鉴权、防盗链签名 URL。
  • IP 黑白名单。
  • 频率限制。
  • WAF 规则。
  • DDoS 清洗。

这些能力可以把一部分攻击流量挡在源站之前,但不能替代应用自身的权限校验和输入校验。

CDN 在实际项目中的应用

静态资源加速

这是最典型的前端场景。构建工具会把资源打包成带哈希的文件:

app.8f3a1c.js
vendor.2b91df.css
logo.a82c0f.png

部署时把这些文件上传到对象存储或静态服务器,再由 CDN 分发。页面中引用 CDN 地址:

<link rel="stylesheet" href="https://static.example.com/assets/vendor.2b91df.css">
<script src="https://static.example.com/assets/app.8f3a1c.js"></script>

推荐策略:

  • 带内容哈希的 JS、CSS、图片设置长期缓存,例如 max-age=31536000, immutable
  • HTML 入口文件不要长期强缓存,避免用户一直拿到旧资源引用。
  • 发布新版本时生成新文件名,而不是覆盖旧文件。

图片和媒体分发

图片、音视频体积大,对带宽和加载体验影响明显。CDN 常用于:

  • 图片压缩和格式转换,例如 WebP、AVIF。
  • 根据设备宽度返回不同尺寸图片。
  • 视频分片分发。
  • 下载限速和防盗链。

图片资源通常适合 CDN 缓存,但用户头像、私有图片、付费内容需要做好鉴权、防盗链或私有访问控制。

第三方库托管

有些项目会通过 CDN 引入 React、Vue、Lodash 等公共库:

<script src="https://cdn.example.com/react.production.min.js"></script>

这种方式可以减少自身构建体积,也可能利用公共缓存。但它也有风险:

  • 第三方 CDN 故障会影响页面可用性。
  • 版本地址如果不固定,可能引入不可控变更。
  • 需要考虑 SRI 完整性校验和 CSP。
  • 在现代工程化项目中,核心依赖通常更推荐打包进自己的构建产物,或使用受控的私有 CDN。

灰度发布与多环境资源

CDN 可以配合构建版本号、路径前缀或边缘规则做灰度。例如:

/assets/prod/app.hash.js
/assets/canary/app.hash.js

也可以通过不同域名区分环境:

static.example.com
static-staging.example.com

但要注意缓存隔离,避免测试资源被线上页面引用,或线上缓存污染测试环境。

常见问题与排查思路

为什么资源更新后用户仍然看到旧版本

常见原因:

  • HTML 被强缓存,仍然引用旧 JS 和 CSS。
  • CDN 节点缓存未刷新。
  • 浏览器缓存仍然有效。
  • 文件名没有内容哈希,覆盖发布导致缓存不可控。
  • 中间代理或 Service Worker 还有缓存。

排查时可以看响应头中的 Cache-ControlAgeETagLast-ModifiedViaX-Cache 等字段。

为什么 CDN 命中率很低

常见原因:

  • URL 带大量随机参数。
  • 缓存时间太短。
  • 响应头禁止缓存。
  • 动态资源被误接入 CDN 但无法共享缓存。
  • 不同请求头、Cookie 导致缓存键过细。
  • 资源访问分散,没有形成热点。

为什么访问 CDN 反而变慢

可能原因:

  • DNS 调度不准确。
  • 用户到节点网络质量差。
  • 节点缓存未命中,经常回源。
  • 源站回源链路慢。
  • HTTPS 握手、证书链、HTTP/2/HTTP/3 配置不合理。
  • 资源本身体积过大,没有压缩或图片优化。

CDN 加速的是链路和缓存,不会自动修复资源过大、接口慢、前端渲染慢等问题。

CDN 会不会缓存错误响应

可能会,取决于 CDN 配置和响应头。某些 CDN 可以配置是否缓存 404301302500 等响应,以及缓存多久。错误缓存有时能减少源站压力,但也可能放大一次发布错误的影响,所以需要谨慎设置。

面试中的标准回答

如果面试官问“CDN 是什么,它的工作原理是什么”,可以这样回答:

CDN 是内容分发网络,它通过在各地部署边缘节点,把静态资源或可缓存内容分发到离用户更近的位置。用户访问 CDN 域名时,DNS 或调度系统会根据地域、运营商、节点健康和负载等因素返回合适的边缘节点 IP。浏览器与该节点建立连接并请求资源,如果节点缓存命中,就直接返回;如果没有命中或缓存过期,节点会向源站回源,拿到资源后按缓存策略保存,再返回给用户。CDN 的主要价值是降低延迟、提升访问速度、减少源站压力,并增强可用性和安全防护。

如果继续追问“CDN 的 CNAME 配在哪里”,可以这样回答:

通常配置在业务加速域名所属 DNS 区域的权威 DNS 中。例如 static.example.com 通常只是 example.com 区域下的一条记录,所以是在 example.com 的权威 DNS 里配置 static.example.com CNAME static.example.com.cdnvendor.net。只有当 static.example.com 被单独委派成一个子区域时,才会由它自己的权威 DNS 管理。

如果继续追问“请求 CDN 资源的链路”,可以按顺序回答:

  1. 浏览器解析页面,发现需要加载 CDN 资源。
  2. 对 CDN 域名做 DNS 解析。
  3. DNS 通过 CNAME 进入 CDN 调度系统。
  4. 调度系统返回合适的边缘节点 IP。
  5. 浏览器和 CDN 节点建立 TCP/TLS 连接。
  6. 浏览器发送 HTTP 请求。
  7. CDN 节点检查缓存。
  8. 命中则直接返回。
  9. 未命中则回源拉取资源。
  10. CDN 缓存资源并返回给浏览器。

总结

CDN 的本质是“分布式边缘缓存 + 智能调度 + 回源机制”。它通过把资源放到离用户更近的位置,减少网络距离、提高响应速度,并把大量重复请求挡在源站之外。

在实际项目中,CDN 最常用于静态资源、图片、音视频和下载文件分发。前端工程里尤其要注意资源哈希、缓存头、HTML 缓存策略、刷新预热、HTTPS 配置和缓存键设计。CDN 用得好,可以显著提升加载体验;用得不谨慎,也可能带来旧资源、缓存污染、隐私数据泄露和排查复杂度上升等问题。