Skip to content
Charles Shao
Go back

CDN:就近访问、回源、缓存策略与防盗链

views

静态资源又大又常被访问,若全打到源站,带宽和延迟都会吃紧。CDN(内容分发网络) 把图片、视频、JS/CSS 等推到各地边缘节点,让用户就近取——像物流把货放到离你最近的仓。

本篇讲清 CDN 与全站加速的差别、回源/预热/刷新的流程、GSLB 如何选节点,以及防盗链机制与为何多数团队直接用云厂商 CDN。

TL;DR

Table of contents

Open Table of contents

1. CDN:静态资源的就近分发

CDN(Content Delivery Network / Content Distribution Network,内容分发网络)的核心思路是:将静态资源分发到部署在多个地理位置的边缘节点,让用户从最近的节点获取资源,而非每次都访问远端源站

从名称拆解来看:

这个思路与现代物流体系高度相似:

CDN 与仓储物流类比:静态资源像商品,分发到各地仓库以便就近配送

静态资源像商品,分发到各地「仓」以便就近取。

从架构角度,CDN 可以看作部署在服务上层的特殊缓存层,专门承接静态资源请求,避免这类高频大体积请求对源站造成压力。

CDN 作为服务上层的特殊缓存层,分布在各地处理静态资源请求

CDN 是服务上层的特殊缓存层,专吃静态请求。

CDN ≠ 全站加速。全站加速(腾讯云称 ECDN、阿里云称 DCDN)可以同时加速静态资源和动态接口;而标准 CDN 主要针对静态资源,对动态请求收益有限。

CDN 与全站加速对比:CDN 主攻静态资源,全站加速可同时加速动静态

CDN 主攻静态;全站加速可同时加速动静态。

绝大部分公司会在项目中使用 CDN 服务,但极少自建。基于成本、稳定性和运维复杂度考量,建议直接选择云厂商(如阿里云、腾讯云、华为云)或专业 CDN 厂商(如网宿、蓝汛)提供的开箱即用服务。

选择云厂商 CDN 时可参考以下维度:

一句话:多地部署服务是为了高可用;CDN 是为了静态资源的就近访问——两件事,不要混为一谈。

同一服务在多地部署多份(同城灾备、异地灾备、多活)解决的是高可用与容灾,与 CDN 的就近访问目标不同,成本也高得多。

CDN 通常也不具备计算能力(Edge Function / Workers 是近年兴起的增强能力,不在本文讨论范围内),它的本质仍是带存储的分发网络,而非分布式计算层。理解这一边界,有助于在架构选型时做出正确取舍:需要计算下沉用边缘函数,需要静态资源加速用 CDN,需要服务高可用用多活部署——三者服务不同目标,通常组合使用。

2. 预热、回源、调度与防盗链

理解 CDN 的工作原理,可以从三个问题入手:

  1. 静态资源是如何被缓存到 CDN 节点的?
  2. 如何找到最合适的 CDN 节点?
  3. 如何防止静态资源被盗用?

2.1 预热、回源与刷新

预热(Prefetch/Warm-up):在用户访问前,主动将源站资源同步到各边缘节点。预热后,用户首次请求即可命中节点缓存,无需回源,降低源站压力并提升首次访问速度。

回源(Origin Pull):若 CDN 节点上没有用户请求的资源,或该资源的缓存已过期,节点将主动向源站拉取最新内容——这个过程称为回源。发生回源时,该请求的响应时间通常比直连源站还慢,因为链路上多了一层 CDN 节点的转发。

刷新(Purge/Refresh):当源站资源更新后,可主动触发刷新,强制清除 CDN 节点上的旧缓存,使下一次请求重新回源获取最新内容。

环节触发时机目的典型耗时/影响
预热上线前或大促前主动触发避免首次访问回源,降低首访延迟与源站压力视资源总量,通常数分钟到数十分钟完成同步
回源节点 miss 或缓存已过期从源站拉取最新内容并写入节点缓存该次请求变慢(多一跳);后续请求命中变快
刷新源站内容更新后手动/API 触发强制清除节点旧缓存,确保下次回源取到最新内容全网生效通常数秒到数十秒,取决于节点规模

命中率与回源率是衡量 CDN 服务质量的两个核心指标:命中率越高越好,回源率越低越好。命中率偏低通常意味着缓存策略(TTL 设置、预热时机)需要优化。

部分 CDN 厂商还支持 stale-while-revalidate 语义:缓存过期时,先返回旧内容(避免用户等待),同时异步向源站拉取最新内容更新缓存——对读延迟敏感、允许短暂展示旧版本的场景(如新闻列表、活动横幅)尤为实用。

2.2 GSLB:如何选出最优节点

GSLB(Global Server Load Balance,全局负载均衡)是 CDN 的调度大脑,负责在所有边缘节点之间协调,将请求引导到最优节点。最常用的实现方式是基于 DNS 的 GSLB

请求调度流程如下:

  1. 浏览器向 DNS 服务器发送域名解析请求;
  2. DNS 服务器根据 CNAME 记录将请求转发给 CDN 专属 DNS(GSLB);
  3. GSLB 综合请求来源 IP、各边缘节点负载、响应时间、带宽等指标,选出最优节点,将其地址返回给浏览器;
  4. 浏览器直接向该边缘节点发起资源请求。

CDN 请求流程示意:用户就近命中边缘节点,未命中则回源拉取并缓存

DNS/GSLB 指路到边缘节点;miss 则回源再缓存。

实际实现中,GSLB 内部通常是 CDN 专属 DNS 与负载均衡系统的组合:DNS 返回负载均衡系统的 IP,浏览器再通过负载均衡系统定位到具体的边缘节点。

2.3 防盗链与访问控制

CDN 分发的内容默认为公开资源,任何人拿到 URL 均可访问。若资源被大量盗刷,将产生不必要的带宽费用。常见的访问控制手段包括:

大多数场景下,URL 时间戳鉴权 是性价比最高的防盗链方案:对 CDN 节点透明、无需回源鉴权、且签名难以伪造。Referer 防盗链可以与其叠加,但 Referer 头容易被伪造,不应作为唯一防线。

3. 缓存策略与 HTTP 头

3.1 Cache-Control 与 TTL

浏览器缓存行为由 Cache-Control / Expires 响应头控制,CDN 节点同样遵守这些头信息——但各厂商的 CDN 可能在平台配置页额外叠加或覆盖源站 TTL。

响应头配置CDN 行为浏览器行为适用场景
Cache-Control: max-age=31536000, immutable缓存 1 年1 年内不重新验证内容哈希文件名(app.3f8a2b.js
Cache-Control: max-age=300缓存 5 分钟5 分钟后重新验证频繁更新的半静态资源
Cache-Control: no-store不缓存不缓存敏感接口、实时数据
Cache-Control: no-cache缓存但每次须向源站验证(ETag)同左需强一致但希望节省带宽

哈希文件名 + 长 TTL 是静态资源的最佳实践:Webpack/Vite 等构建工具默认为打包产物生成内容哈希后缀(如 main.a1b2c3.js)。文件内容不变则 URL 不变,CDN 可安全缓存 1 年;内容更新则 URL 变化,旧缓存自然淘汰,无需手动刷新。

以一次真实响应为例,静态资源与 HTML 入口的响应头配置应明显不同:

# 静态资源:内容哈希文件名,一年内不重新验证
GET /assets/app.3f8a2b.js HTTP/1.1

HTTP/1.1 200 OK
Content-Type: application/javascript
Cache-Control: public, max-age=31536000, immutable
ETag: "3f8a2b1a2b3c"
# HTML 入口:内容会变,必须每次向源站验证
GET /index.html HTTP/1.1

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
ETag: "e1a2b39f8e7d"

广告创意、商品图、视频封面等媒体素材同理——只要 URL 带内容哈希(或素材一经发布不再原地覆盖),就可放心设长 TTL 让 CDN 尽量吃满命中:

# 广告创意素材:图片/视频封面,内容哈希 URL,长 TTL 吃满命中率
GET /creatives/banner.9f3c1a.jpg HTTP/1.1

HTTP/1.1 200 OK
Content-Type: image/jpeg
Cache-Control: public, max-age=2592000, immutable   # 30 天
Access-Control-Allow-Origin: *                        # 跨域展位需要
Vary: Accept                                          # 按 WebP/AVIF 协商时才加

no-cache 不等于不缓存——它表示缓存但每次必须带 If-None-Match 向源站验证,验证通过(304)时无需回传响应体,兼顾新鲜度与带宽;若要求完全不缓存(如敏感接口、计费像素),应使用 no-store

3.2 源站侧的缓存头配置(Nginx)

CDN 的缓存行为最终由源站响应头驱动。以 Nginx 作为素材源站为例,应按路径下发差异化的 Cache-Control

# 素材源站:按目录区分「可长缓存」与「绝不缓存」
server {
    listen 80;
    server_name origin.example-cdn.com;
    root /data/assets;

    # 带内容哈希的创意素材 → 允许 CDN 与浏览器长期缓存
    location ~* ^/creatives/.+\.(?:jpg|jpeg|png|webp|avif|mp4|js|css)$ {
        add_header Cache-Control "public, max-age=2592000, immutable";
        add_header Access-Control-Allow-Origin "*";
        access_log off;
    }

    # VAST 文档:可能含竞价宏/追踪 URL,短 TTL 且必须回源校验
    location ~* ^/vast/.+\.xml$ {
        add_header Cache-Control "public, max-age=60";
    }

    # 计费/曝光像素:绝不缓存,每次都要真实打到 Collector
    location ~* ^/px/ {
        add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
        add_header Pragma "no-cache";
        expires -1;
    }
}

源站头与 CDN 平台配置双重生效:多数云厂商 CDN 支持在控制台按路径覆盖或叠加 TTL。团队约定应以「源站头为准、平台配置只做兜底」,避免两侧各配一套导致缓存行为难以预期。更多 Nginx 配置见 Nginx 配置实践

3.3 动态内容为何不宜缓存

API 接口通常返回个性化或实时数据(购物车、订单状态、实时价格),这类内容:

对于 API,正确做法是在 CDN 配置中将动态路径(如 /api/*绕过缓存直接回源,或使用全站加速(DCDN/ECDN)的动态加速通道(TCP 复用、BGP 最优路由),而非尝试在 CDN 层缓存 API 响应。

3.4 Origin Shield(回源盾)

大规模 CDN 部署中,当缓存集中失效或突发流量到来时,来自全国各地多个 POP 节点的回源请求会同时打向源站,产生回源风暴

Origin Shield(回源盾 / 中间层缓存节点) 是解法:在各边缘 POP 节点与源站之间增加一层集中式中间节点。所有 POP 的回源请求先汇聚到中间节点,中间节点再以单一请求向源站拉取,将 N 次回源合并为 1 次,大幅降低源站压力——相关内容也可与 缓存机制 中的缓存雪崩对比理解。

4. 关键指标

指标含义健康参考
命中率(Hit Ratio)边缘节点直接返回的请求比例> 90%(图片/JS/CSS 类资源)
回源带宽CDN 向源站拉取内容消耗的带宽越低越好;命中率低时回源带宽高
TTFB(首字节时间)客户端到收到第一个响应字节的时间命中时 < 50ms;回源时 200–500ms+
边缘节点 QPS所有 POP 节点合计请求量用于容量规划与费用预估

命中率偏低的常见原因

  1. TTL 太短:资源频繁过期,每次都需回源;
  2. URL 带动态参数:资源 URL 含时间戳 ?v=1234567?_=<random>,导致每个请求都是「新 key」,无法复用缓存;
  3. 预热不足:新上线版本没有预热,用户访问全量回源。

监控建议:在云厂商 CDN 控制台配置命中率与回源带宽的告警阈值(如命中率跌破 85% 触发告警),能在用户感知之前发现缓存策略问题。大多数 CDN 厂商的控制台也支持按域名、路径、地区维度下钻分析,便于定位具体是哪类资源命中率低。

5. 常见故障模式

5.1 集中过期引发回源风暴

当大量缓存同时到期(如 CDN 节点重启、TTL 统一设置导致集中失效),所有边缘节点同时向源站发起回源,在极短时间内产生数倍于平时的源站流量。

应对

5.2 刷新后缓存未及时失效

手动触发缓存刷新(Purge)后,理论上边缘节点应立即删除旧内容。但在以下情况下可能出现「刷新了但用户仍看到旧版本」:

应对

6. 生产清单与反模式

6.1 启用 CDN 的实践清单

资源类型建议策略说明
JS / CSS(内容哈希文件名)TTL 365 天 + immutable构建工具自动生成哈希名,更新即换 URL
图片 / 字体TTL 7–30 天变更频率低,较长 TTL 可有效提升命中率
HTML(入口文件)TTL 0–5 分钟 或 no-cacheHTML 引用带哈希的资源,本身需保持新鲜
API 接口(/api/*不缓存 / 绕过 CDN动态数据不应被缓存(§3.3)
大体积下载文件TTL 7 天 + 分片传输支持减少重复回源;Content-Disposition 保证正确下载行为

上线前 checklist

6.2 反模式(Anti-patterns)

以下都是生产中真实踩过、且代价不小的坑,逐条对照排查:

反模式后果正确做法
回源无收敛:不开 Origin Shield,各 POP 各自回源缓存集中失效/大 campaign 冷启动时回源风暴打垮源站开 Origin Shield 合并回源 + 差异化 TTL + 源站限流(§3.4、§5.1)
ignore query string 一刀切开全站动态/计费路径被错误归一化,唯一像素被当同一 key 缓存仅对纯静态资源开启;tracking/API 路径显式关闭
URL 带随机参数当缓存 key每次都是新 key,命中率≈0,形同没上 CDN内容哈希文件名;多尺寸/多格式用不同 URL 而非查询参数
Referer 防盗链当唯一防线Referer 易伪造,防不住盗刷与盗用创意签名 URL(时间戳鉴权)为主,Referer 叠加为辅(§2.3)
HTML 入口配长 TTL发版后用户长时间看到旧页面、加载到已删除的旧资源HTML no-cache/短 TTL,静态资源哈希命名配长 TTL
源站不自保回源一旦被打满,正常素材也被拖垮(雪崩)回源限流/排队/降级兜底,参见 负载均衡

小结


views
Share this post on:

Previous Post
Nginx 配置实践:结构、反向代理、限流与生产清单
Next Post
数据库性能与扩展:索引、读写分离与分库分表