文章目录
-
- [一、CDN 概述](#一、CDN 概述)
-
- [1.1 CDN 解决什么问题](#1.1 CDN 解决什么问题)
- [1.2 基本工作流程](#1.2 基本工作流程)
- [1.3 核心架构](#1.3 核心架构)
- [1.4 缓存机制](#1.4 缓存机制)
- [1.5 静态加速 vs 动态加速](#1.5 静态加速 vs 动态加速)
- [1.6 路由与协议优化](#1.6 路由与协议优化)
- [1.7 常见应用场景](#1.7 常见应用场景)
- [二、什么情况下可以不上 CDN](#二、什么情况下可以不上 CDN)
-
- [2.1 可以不上 CDN 的情况](#2.1 可以不上 CDN 的情况)
- [2.2 即使都在同一城市,CDN 仍可能有价值](#2.2 即使都在同一城市,CDN 仍可能有价值)
- [2.4 决策速查表](#2.4 决策速查表)
- [三、CDN 多节点缓存不一致问题](#三、CDN 多节点缓存不一致问题)
-
- [3.1 为什么会不一致?](#3.1 为什么会不一致?)
- [3.2 常见触发场景](#3.2 常见触发场景)
- [3.3 解决方案](#3.3 解决方案)
- [3.4 实际工程中最常用的做法](#3.4 实际工程中最常用的做法)
- [四、CDN 调度系统](#四、CDN 调度系统)
-
- [4.1 谁在提供调度服务?](#4.1 谁在提供调度服务?)
- [4.2 调度系统具体做什么?](#4.2 调度系统具体做什么?)
- [4.3 一句话理解](#4.3 一句话理解)
- [五、访问 CDN 的网络链路](#五、访问 CDN 的网络链路)
-
- [5.1 一次完整的 CDN 访问链路](#5.1 一次完整的 CDN 访问链路)
- [5.2 回源链路可能有层级](#5.2 回源链路可能有层级)
- [5.3 动态请求链路略有不同](#5.3 动态请求链路略有不同)
- [5.4 一句话串起来](#5.4 一句话串起来)
- [六、如何判断 CDN 是否生效](#六、如何判断 CDN 是否生效)
-
- [6.1 第一步:看 DNS ------ 请求是否走到了 CDN](#6.1 第一步:看 DNS —— 请求是否走到了 CDN)
- [6.2 第二步:看响应头 ------ 资源是否命中 CDN 缓存](#6.2 第二步:看响应头 —— 资源是否命中 CDN 缓存)
- [6.3 第三步:看 Remote Address ------ 是否连接到 CDN 节点](#6.3 第三步:看 Remote Address —— 是否连接到 CDN 节点)
- [6.4 分层判断 CDN 是否真正"起作用"](#6.4 分层判断 CDN 是否真正"起作用")
- [6.5 实用排查顺序](#6.5 实用排查顺序)
- [七、CDN 缓存与浏览器缓存的关系](#七、CDN 缓存与浏览器缓存的关系)
-
- [7.1 强缓存 vs 协商缓存](#7.1 强缓存 vs 协商缓存)
- [7.2 三级缓存判断流程](#7.2 三级缓存判断流程)
- [7.3 关键结论](#7.3 关键结论)
- [八、防盗链与 URL 鉴权](#八、防盗链与 URL 鉴权)
-
- [8.1 常见防护方式对比](#8.1 常见防护方式对比)
- [8.2 URL 鉴权的签名思路](#8.2 URL 鉴权的签名思路)
- 九、回源细节与常见坑
-
- [9.1 回源 HOST](#9.1 回源 HOST)
- [9.2 回源协议](#9.2 回源协议)
- [9.3 Range 回源(分片缓存)](#9.3 Range 回源(分片缓存))
- [9.4 获取用户真实 IP](#9.4 获取用户真实 IP)
- 十、命中率、监控与计费
-
- [10.1 命中率怎么算](#10.1 命中率怎么算)
- [10.2 关键监控指标](#10.2 关键监控指标)
- [10.3 常见计费方式](#10.3 常见计费方式)
- [十一、接入 CDN 的标准流程](#十一、接入 CDN 的标准流程)
- 十二、常见问题排查(FAQ)
一、CDN 概述
CDN(Content Delivery Network,内容分发网络)本质是在互联网上多加一层分布式节点,把内容缓存到离用户更近的地方,让用户就近获取,从而降低延迟、减轻源站压力。
1.1 CDN 解决什么问题
- 降低延迟:用户不再每次都访问远端源站,而是访问最近的边缘节点。
- 减轻源站压力:图片、CSS、JS、视频等静态资源由边缘节点承担,源站只需处理回源和动态请求。
- 提升可用性:多节点冗余,某个节点故障时可以切换到其他节点。
- 改善跨运营商/跨地域访问:比如电信用户访问联通源站时,可通过同运营商边缘节点加速。
1.2 基本工作流程
- 用户访问域名。
- DNS 解析被指向 CDN 调度系统。
- 调度系统根据用户 IP、地域、运营商、节点负载等选择最优边缘节点。
- 边缘节点如果有有效缓存,直接返回。
- 如果没有缓存或缓存过期,就回源站拉取内容,再返回给用户并缓存。
#mermaid-svg-XWRw8PH5VJiYbcZM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XWRw8PH5VJiYbcZM .error-icon{fill:#552222;}#mermaid-svg-XWRw8PH5VJiYbcZM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XWRw8PH5VJiYbcZM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XWRw8PH5VJiYbcZM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XWRw8PH5VJiYbcZM .marker.cross{stroke:#333333;}#mermaid-svg-XWRw8PH5VJiYbcZM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XWRw8PH5VJiYbcZM p{margin:0;}#mermaid-svg-XWRw8PH5VJiYbcZM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster-label text{fill:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster-label span{color:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster-label span p{background-color:transparent;}#mermaid-svg-XWRw8PH5VJiYbcZM .label text,#mermaid-svg-XWRw8PH5VJiYbcZM span{fill:#333;color:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM .node rect,#mermaid-svg-XWRw8PH5VJiYbcZM .node circle,#mermaid-svg-XWRw8PH5VJiYbcZM .node ellipse,#mermaid-svg-XWRw8PH5VJiYbcZM .node polygon,#mermaid-svg-XWRw8PH5VJiYbcZM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XWRw8PH5VJiYbcZM .rough-node .label text,#mermaid-svg-XWRw8PH5VJiYbcZM .node .label text,#mermaid-svg-XWRw8PH5VJiYbcZM .image-shape .label,#mermaid-svg-XWRw8PH5VJiYbcZM .icon-shape .label{text-anchor:middle;}#mermaid-svg-XWRw8PH5VJiYbcZM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XWRw8PH5VJiYbcZM .rough-node .label,#mermaid-svg-XWRw8PH5VJiYbcZM .node .label,#mermaid-svg-XWRw8PH5VJiYbcZM .image-shape .label,#mermaid-svg-XWRw8PH5VJiYbcZM .icon-shape .label{text-align:center;}#mermaid-svg-XWRw8PH5VJiYbcZM .node.clickable{cursor:pointer;}#mermaid-svg-XWRw8PH5VJiYbcZM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XWRw8PH5VJiYbcZM .arrowheadPath{fill:#333333;}#mermaid-svg-XWRw8PH5VJiYbcZM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XWRw8PH5VJiYbcZM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XWRw8PH5VJiYbcZM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XWRw8PH5VJiYbcZM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XWRw8PH5VJiYbcZM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XWRw8PH5VJiYbcZM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster text{fill:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM .cluster span{color:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XWRw8PH5VJiYbcZM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XWRw8PH5VJiYbcZM rect.text{fill:none;stroke-width:0;}#mermaid-svg-XWRw8PH5VJiYbcZM .icon-shape,#mermaid-svg-XWRw8PH5VJiYbcZM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XWRw8PH5VJiYbcZM .icon-shape p,#mermaid-svg-XWRw8PH5VJiYbcZM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XWRw8PH5VJiYbcZM .icon-shape .label rect,#mermaid-svg-XWRw8PH5VJiYbcZM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XWRw8PH5VJiYbcZM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XWRw8PH5VJiYbcZM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XWRw8PH5VJiYbcZM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 请求域名
2. 返回最优边缘节点 IP
3. 请求资源
命中
未命中
用户
智能调度 DNS / GSLB
边缘节点
缓存命中?
直接返回内容
回源站拉取
并写入缓存
1.3 核心架构
- 源站:存放原始内容,比如网站文件、图片、视频、API 服务。
- 边缘节点:最靠近用户的缓存节点,负责直接响应用户请求。
- 区域/中心节点:作为中间层,承接边缘节点回源,减少直接打到源站的流量。
- 调度系统 / GSLB:全局负载均衡,决定用户请求去哪个节点。
- 回源系统:边缘节点没有缓存时,从源站或上级节点获取内容。
1.4 缓存机制
CDN 加速效果很大程度上取决于缓存命中率。
关键概念:
- TTL:缓存有效期。静态资源可以设置较长 TTL,频繁变化的内容设置较短 TTL。
- Cache-Control / Expires:源站响应头会影响 CDN 缓存行为。
- 缓存键:通常由域名、路径、查询参数等决定。查询参数过多或不规范会降低命中率。
- 缓存预热:提前把热门内容推到边缘节点,避免活动开始时集中回源。
- 缓存刷新:内容更新后,主动清除旧缓存,让用户获取最新版本。
刷新与预热的操作对比:
| 操作 | 作用 | 典型场景 |
|---|---|---|
| URL 刷新 | 强制清除指定 URL 的节点缓存,下次访问回源取最新 | 文件同名覆盖更新 |
| 目录刷新 | 清除整个目录下的缓存 | 批量发版、整站更新 |
| URL 预热 | 主动把资源提前拉到边缘节点,不依赖用户首次访问 | 大促活动、新视频/安装包上线 |
| 刷新 + 预热组合 | 先清旧缓存,再把新内容推到节点 | 重大活动前的准备 |
1.5 静态加速 vs 动态加速
- 静态加速:适合图片、CSS、JS、字体、视频、安装包等。命中缓存后基本不回源。
- 动态加速:适合 API、登录态、个性化数据等不能长期缓存的内容。主要通过更优路由、协议优化、连接复用来降低延迟。
1.6 路由与协议优化
- 智能 DNS 调度:根据用户位置、运营商、节点状态选择节点。
- Anycast / BGP 优化:让用户请求走更优网络路径。
- HTTP/2、HTTP/3、QUIC:减少连接建立开销,提升弱网体验。
- TCP 优化:如拥塞控制算法优化,提升长距离传输效率。
1.7 常见应用场景
- 网站静态资源加速
- 视频点播 / 直播加速
- 游戏、APP 安装包分发
- API 动态加速
- 跨境电商、出海业务加速
- DDoS 防护、WAF 安全加速
二、什么情况下可以不上 CDN
2.1 可以不上 CDN 的情况
如果服务同时满足以下条件,不上 CDN 完全没问题:
- 用户基本都在杭州本地,或者都在同一城市、同一办公楼
- 访问量不大,服务器带宽和性能扛得住
- 没有大量图片、视频等大静态资源
- 不需要防 DDoS、WAF 等安全防护
- 纯内部系统或小型项目
这种情况下,CDN 带来的加速感知确实不明显,属于"微优化",没必要为了上而上。
2.2 即使都在同一城市,CDN 仍可能有价值
- 跨运营商问题:杭州用户可能用电信、联通、移动等不同运营商。如果服务器在阿里云(电信机房),移动用户访问时可能要走跨运营商链路,CDN 可以解决这个。
- 抗并发冲击:即使用户都在本地,如果突然流量暴增(比如促销活动),CDN 能分担源站压力,避免带宽被打满。
- 隐藏源站 IP:CDN 可以作为一层保护,源站 IP 不直接暴露,提升安全性。
- 浏览器并发优化:静态资源走 CDN 域名,可以绕过浏览器对同一域名的并发连接数限制(通常 4-6 个),提升页面加载速度。
2.4 决策速查表
| 你的情况 | 建议 |
|---|---|
| 用户集中本地、流量小、无大静态资源 | 可以不上 |
| 存在明显跨运营商访问问题 | 建议上 |
| 有促销/活动导致流量突增 | 建议上 |
| 有大量图片、视频、安装包分发 | 强烈建议上 |
| 需要防 DDoS、隐藏源站 IP | 建议上 |
| 静态资源想绕过浏览器单域名并发限制 | 可以考虑上 |
三、CDN 多节点缓存不一致问题
3.1 为什么会不一致?
核心原因是:CDN 各边缘节点是独立缓存的,不会自动互相同步。
同一个资源 URL(如 https://cdn.example.com/app.js)可能被北京节点、上海节点、广州节点分别缓存。每个节点缓存的时间点不同,TTL 到期时间也不同。源站更新文件后,如果 URL 没变,就容易出现:
| 节点状态 | 返回结果 |
|---|---|
| 缓存未过期 | 返回旧版本 |
| 缓存已过期 | 回源拉取新版本 |
| 从未缓存过 | 直接回源拉取新版本 |
于是不同用户访问同一个 URL,可能拿到不同版本。
#mermaid-svg-6Uc27GQuUnAU4IGI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6Uc27GQuUnAU4IGI .error-icon{fill:#552222;}#mermaid-svg-6Uc27GQuUnAU4IGI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Uc27GQuUnAU4IGI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Uc27GQuUnAU4IGI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6Uc27GQuUnAU4IGI .marker.cross{stroke:#333333;}#mermaid-svg-6Uc27GQuUnAU4IGI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6Uc27GQuUnAU4IGI p{margin:0;}#mermaid-svg-6Uc27GQuUnAU4IGI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster-label text{fill:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster-label span{color:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster-label span p{background-color:transparent;}#mermaid-svg-6Uc27GQuUnAU4IGI .label text,#mermaid-svg-6Uc27GQuUnAU4IGI span{fill:#333;color:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI .node rect,#mermaid-svg-6Uc27GQuUnAU4IGI .node circle,#mermaid-svg-6Uc27GQuUnAU4IGI .node ellipse,#mermaid-svg-6Uc27GQuUnAU4IGI .node polygon,#mermaid-svg-6Uc27GQuUnAU4IGI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Uc27GQuUnAU4IGI .rough-node .label text,#mermaid-svg-6Uc27GQuUnAU4IGI .node .label text,#mermaid-svg-6Uc27GQuUnAU4IGI .image-shape .label,#mermaid-svg-6Uc27GQuUnAU4IGI .icon-shape .label{text-anchor:middle;}#mermaid-svg-6Uc27GQuUnAU4IGI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6Uc27GQuUnAU4IGI .rough-node .label,#mermaid-svg-6Uc27GQuUnAU4IGI .node .label,#mermaid-svg-6Uc27GQuUnAU4IGI .image-shape .label,#mermaid-svg-6Uc27GQuUnAU4IGI .icon-shape .label{text-align:center;}#mermaid-svg-6Uc27GQuUnAU4IGI .node.clickable{cursor:pointer;}#mermaid-svg-6Uc27GQuUnAU4IGI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6Uc27GQuUnAU4IGI .arrowheadPath{fill:#333333;}#mermaid-svg-6Uc27GQuUnAU4IGI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6Uc27GQuUnAU4IGI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6Uc27GQuUnAU4IGI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Uc27GQuUnAU4IGI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6Uc27GQuUnAU4IGI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Uc27GQuUnAU4IGI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster text{fill:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI .cluster span{color:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6Uc27GQuUnAU4IGI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6Uc27GQuUnAU4IGI rect.text{fill:none;stroke-width:0;}#mermaid-svg-6Uc27GQuUnAU4IGI .icon-shape,#mermaid-svg-6Uc27GQuUnAU4IGI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Uc27GQuUnAU4IGI .icon-shape p,#mermaid-svg-6Uc27GQuUnAU4IGI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6Uc27GQuUnAU4IGI .icon-shape .label rect,#mermaid-svg-6Uc27GQuUnAU4IGI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Uc27GQuUnAU4IGI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6Uc27GQuUnAU4IGI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6Uc27GQuUnAU4IGI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 返回 v1
回源拿到 v2
直接回源 v2
源站:app.js 已更新为 v2
北京节点
缓存 v1 未过期
上海节点
缓存已过期
广州节点
从未缓存
用户 A 看到 v1
用户 B 看到 v2
用户 C 看到 v2
3.2 常见触发场景
- 源站用同名文件覆盖旧文件 :比如重新上传了
app.js,但文件名没变。 - 各节点 TTL 到期时间不同:有的节点刚缓存,有的快过期了。
- 缓存键配置不当:比如 CDN 配置了"忽略全部参数",但源站会根据参数返回不同内容,不同节点就可能缓存出不同结果。
- 只刷新了部分节点:比如只刷新了某个目录,但个别 URL 没覆盖到。
3.3 解决方案
-
文件名带版本号或哈希
例如:
/app.v1.js、/app.abc123.js这样每次发布都是新 URL,CDN 天然不会混用旧缓存。
-
URL 加版本参数
例如:
/app.js?v=2但要注意,如果 CDN 配置了"忽略查询参数",这个参数可能不会生效,需要确认缓存键规则。
-
发布后主动刷新缓存
更新源站后,通过 CDN 控制台或 API 刷新对应 URL、目录或正则路径。如果是同名文件覆盖,建议使用强制刷新,避免节点回源时仍命中 304 而继续使用旧缓存。
-
合理设置缓存键
如果源站会根据参数返回不同内容,就不要简单配置"忽略全部参数"。
-
对重要资源设置较短 TTL
比如 HTML、配置接口、经常变更的静态资源,不要设置太长的缓存时间。
3.4 实际工程中最常用的做法
前端静态资源一般用 文件名哈希 + 长 TTL ;HTML 或配置类资源用 短 TTL 或发布后刷新。这样既能保证加速效果,又能避免多节点缓存不一致。
四、CDN 调度系统
4.1 谁在提供调度服务?
CDN 调度系统通常是由 CDN 厂商自己的调度服务 在提供服务,而不是某个通用公共系统。它在技术上也叫 GSLB / 全局负载均衡,可以理解为 CDN 的"大脑"。
4.2 调度系统具体做什么?
当用户访问一个接入 CDN 的域名时,调度系统主要做这些事:
- 接管 DNS 解析:你的域名会 CNAME 到 CDN 厂商提供的调度域名。
- 判断用户从哪里来:根据用户 IP 判断城市、运营商、网络类型。
- 选择最优节点:综合考虑地理位置、节点负载、链路延迟、丢包率、节点健康状态等。
- 返回最近节点 IP:用户拿到 IP 后,直接向这个边缘节点请求内容。
- 故障时自动切换:如果某个节点挂了,调度系统会把后续请求切到其他正常节点。
4.3 一句话理解
CDN 调度系统 = CDN 厂商提供的智能 DNS / GSLB 服务。 它负责在用户访问的第一时间,把请求"指路"到最合适的边缘节点;边缘节点则负责真正缓存和返回内容。
五、访问 CDN 的网络链路
5.1 一次完整的 CDN 访问链路
以域名 www.example.com 接入 CDN 为例:
- 用户访问域名 :浏览器请求
https://www.example.com/index.html。 - 向本地 DNS 发起解析 :浏览器先问本地 DNS:
www.example.com的 IP 是多少? - 域名返回 CNAME 记录 :域名的权威 DNS 发现这个域名已经接入 CDN,于是返回一个 CNAME,例如:
www.example.com.c.cdnhwc1.com。 - 解析请求进入 CDN 调度系统:本地 DNS 继续解析这个 CNAME,请求会到达 CDN 厂商的智能 DNS / GSLB。
- 调度系统选择最优节点:调度系统根据用户位置、运营商、节点健康状态、节点负载、链路延迟等,选出一个最合适的边缘节点 IP。
- 返回边缘节点 IP:CDN 调度系统把这个节点 IP 返回给本地 DNS,本地 DNS 再返回给用户浏览器。
- 用户向边缘节点发起 HTTP/HTTPS 请求:浏览器拿到 IP 后,直接向这个 CDN 边缘节点请求资源。
- 边缘节点判断是否命中缓存 :
- 命中缓存:边缘节点直接返回资源给用户,速度最快。
- 未命中缓存:边缘节点回源站拉取资源,缓存后再返回给用户。
完整链路时序图:
源站 边缘节点 CDN 调度系统 权威 DNS 本地 DNS 用户浏览器 源站 边缘节点 CDN 调度系统 权威 DNS 本地 DNS 用户浏览器 #mermaid-svg-CfvWKpLm55EXHZKk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CfvWKpLm55EXHZKk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CfvWKpLm55EXHZKk .error-icon{fill:#552222;}#mermaid-svg-CfvWKpLm55EXHZKk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CfvWKpLm55EXHZKk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CfvWKpLm55EXHZKk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CfvWKpLm55EXHZKk .marker.cross{stroke:#333333;}#mermaid-svg-CfvWKpLm55EXHZKk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CfvWKpLm55EXHZKk p{margin:0;}#mermaid-svg-CfvWKpLm55EXHZKk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CfvWKpLm55EXHZKk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-CfvWKpLm55EXHZKk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CfvWKpLm55EXHZKk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-CfvWKpLm55EXHZKk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-CfvWKpLm55EXHZKk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-CfvWKpLm55EXHZKk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-CfvWKpLm55EXHZKk .sequenceNumber{fill:white;}#mermaid-svg-CfvWKpLm55EXHZKk #sequencenumber{fill:#333;}#mermaid-svg-CfvWKpLm55EXHZKk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-CfvWKpLm55EXHZKk .messageText{fill:#333;stroke:none;}#mermaid-svg-CfvWKpLm55EXHZKk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CfvWKpLm55EXHZKk .labelText,#mermaid-svg-CfvWKpLm55EXHZKk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-CfvWKpLm55EXHZKk .loopText,#mermaid-svg-CfvWKpLm55EXHZKk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-CfvWKpLm55EXHZKk .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CfvWKpLm55EXHZKk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-CfvWKpLm55EXHZKk .noteText,#mermaid-svg-CfvWKpLm55EXHZKk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-CfvWKpLm55EXHZKk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CfvWKpLm55EXHZKk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CfvWKpLm55EXHZKk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CfvWKpLm55EXHZKk .actorPopupMenu{position:absolute;}#mermaid-svg-CfvWKpLm55EXHZKk .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-CfvWKpLm55EXHZKk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CfvWKpLm55EXHZKk .actor-man circle,#mermaid-svg-CfvWKpLm55EXHZKk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-CfvWKpLm55EXHZKk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt 缓存命中 缓存未命中 请求解析 www.example.com 1 递归查询 2 返回 CNAME(CDN 厂商调度域名) 3 继续解析 CNAME 4 按地域/运营商/负载返回最优节点 IP 5 返回边缘节点 IP 6 发起 HTTP/HTTPS 请求 7 直接返回资源(X-Cache: HIT) 8 回源拉取 9 返回内容 10 返回资源并缓存(X-Cache: MISS) 11
5.2 回源链路可能有层级
很多 CDN 不是"边缘节点直接回源站",而是分级缓存:
用户 → L1 边缘节点 → L2 区域节点 → 源站
如果 L1 边缘节点没有缓存,可能先去 L2 区域节点找;L2 也没有,再回源站。这样能减少源站压力,也能让同一区域的内容更快分发。
#mermaid-svg-o8zvtmYAkURx77v2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-o8zvtmYAkURx77v2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o8zvtmYAkURx77v2 .error-icon{fill:#552222;}#mermaid-svg-o8zvtmYAkURx77v2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o8zvtmYAkURx77v2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o8zvtmYAkURx77v2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o8zvtmYAkURx77v2 .marker.cross{stroke:#333333;}#mermaid-svg-o8zvtmYAkURx77v2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o8zvtmYAkURx77v2 p{margin:0;}#mermaid-svg-o8zvtmYAkURx77v2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-o8zvtmYAkURx77v2 .cluster-label text{fill:#333;}#mermaid-svg-o8zvtmYAkURx77v2 .cluster-label span{color:#333;}#mermaid-svg-o8zvtmYAkURx77v2 .cluster-label span p{background-color:transparent;}#mermaid-svg-o8zvtmYAkURx77v2 .label text,#mermaid-svg-o8zvtmYAkURx77v2 span{fill:#333;color:#333;}#mermaid-svg-o8zvtmYAkURx77v2 .node rect,#mermaid-svg-o8zvtmYAkURx77v2 .node circle,#mermaid-svg-o8zvtmYAkURx77v2 .node ellipse,#mermaid-svg-o8zvtmYAkURx77v2 .node polygon,#mermaid-svg-o8zvtmYAkURx77v2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-o8zvtmYAkURx77v2 .rough-node .label text,#mermaid-svg-o8zvtmYAkURx77v2 .node .label text,#mermaid-svg-o8zvtmYAkURx77v2 .image-shape .label,#mermaid-svg-o8zvtmYAkURx77v2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-o8zvtmYAkURx77v2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-o8zvtmYAkURx77v2 .rough-node .label,#mermaid-svg-o8zvtmYAkURx77v2 .node .label,#mermaid-svg-o8zvtmYAkURx77v2 .image-shape .label,#mermaid-svg-o8zvtmYAkURx77v2 .icon-shape .label{text-align:center;}#mermaid-svg-o8zvtmYAkURx77v2 .node.clickable{cursor:pointer;}#mermaid-svg-o8zvtmYAkURx77v2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-o8zvtmYAkURx77v2 .arrowheadPath{fill:#333333;}#mermaid-svg-o8zvtmYAkURx77v2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-o8zvtmYAkURx77v2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-o8zvtmYAkURx77v2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o8zvtmYAkURx77v2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-o8zvtmYAkURx77v2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o8zvtmYAkURx77v2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-o8zvtmYAkURx77v2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-o8zvtmYAkURx77v2 .cluster text{fill:#333;}#mermaid-svg-o8zvtmYAkURx77v2 .cluster span{color:#333;}#mermaid-svg-o8zvtmYAkURx77v2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-o8zvtmYAkURx77v2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-o8zvtmYAkURx77v2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-o8zvtmYAkURx77v2 .icon-shape,#mermaid-svg-o8zvtmYAkURx77v2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-o8zvtmYAkURx77v2 .icon-shape p,#mermaid-svg-o8zvtmYAkURx77v2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-o8zvtmYAkURx77v2 .icon-shape .label rect,#mermaid-svg-o8zvtmYAkURx77v2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-o8zvtmYAkURx77v2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-o8zvtmYAkURx77v2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-o8zvtmYAkURx77v2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 未命中
未命中
内容逐级缓存返回
用户
L1 边缘节点
L2 区域节点
源站
5.3 动态请求链路略有不同
如果是 API、登录态、个性化数据这类动态内容,通常不能长期缓存。CDN 会更多做智能路由和协议优化,把请求更快转发到源站,而不是单纯依赖缓存。
5.4 一句话串起来
用户访问 CDN 时,DNS 先把域名解析成"最近边缘节点 IP",用户再向这个节点请求内容;节点有缓存就直接返回,没有就回源站取。
六、如何判断 CDN 是否生效
6.1 第一步:看 DNS ------ 请求是否走到了 CDN
在终端执行:
bash
nslookup www.example.com
或者:
bash
dig www.example.com +short
如果返回的 CNAME 包含 cdn、cloudfront、alicdn、qcloud.com 这类 CDN 厂商域名,或者返回多个不同 IP,通常说明请求已经进入了 CDN 调度。
如果始终只返回一个固定 IP,而且这个 IP 就是你源站服务器,那可能还没真正接入 CDN。
6.2 第二步:看响应头 ------ 资源是否命中 CDN 缓存
打开浏览器开发者工具,进入 Network 面板,刷新页面,选中一个静态资源,查看响应头。
重点看这些字段:
| 字段 | 含义 | 说明 |
|---|---|---|
X-Cache: HIT |
命中 CDN 缓存 | 资源从 CDN 节点返回,没有回源 |
X-Cache: MISS |
未命中缓存 | CDN 节点没有该资源,已回源 |
Cf-Cache-Status: HIT |
Cloudflare 命中 | 仅 Cloudflare 类 CDN 常见 |
Age |
缓存已存在时间 | 值越大,说明资源在 CDN 节点缓存越久 |
Via / X-Served-By |
经过的代理节点 | 常能看到 CDN 厂商标识 |
X-Cache 除了 HIT / MISS,不同厂商还可能出现这些状态:
| 状态 | 含义 |
|---|---|
HIT |
命中节点缓存,直接返回 |
MISS |
未命中,已回源拉取 |
EXPIRED |
缓存已过期,回源获取了新内容 |
STALE |
缓存已过期,但仍先用旧内容响应,同时后台异步更新(stale-while-revalidate) |
BYPASS / DYNAMIC |
该请求不参与缓存,直接转发(常见于动态请求或命中不缓存规则) |
UPDATING |
该资源正在回源更新中,稍后重试通常会命中 |
最直观的判断是:
X-Cache: HIT:CDN 缓存生效。X-Cache: MISS:CDN 在工作,但这次没命中,已经回源。- 完全没有 CDN 相关头:需要确认是否真的接入了 CDN。
也可以用命令行快速检查:
bash
curl -I https://www.example.com/static/app.js
6.3 第三步:看 Remote Address ------ 是否连接到 CDN 节点
在 Network 面板里查看资源的 Remote Address。如果它不是你源站 IP,而是 CDN 厂商的节点 IP,说明请求确实走到了 CDN。
但要注意:走到 CDN 不等于一定命中缓存 。比如第一次访问某个资源,CDN 会回源拉取,这时 X-Cache 通常是 MISS。
6.4 分层判断 CDN 是否真正"起作用"
- 只走到 CDN :DNS 指向 CDN,但
X-Cache: MISS。说明 CDN 已接入,但缓存还没建立。 - 缓存已生效 :
X-Cache: HIT,Age大于 0。说明资源确实从 CDN 节点返回。 - 加速效果明显 :同一资源多次请求,第二次明显更快,且状态为
HIT。 - 未接入 CDN:DNS 仍指向源站 IP,响应头也没有任何 CDN 标识。
6.5 实用排查顺序
- 先
nslookup看域名是否解析到 CDN。 - 再打开 Network 面板看资源是否来自 CDN IP。
- 然后看
X-Cache判断是否命中缓存。 - 最后看
Age和加载时间,确认缓存是否稳定生效。
提示 :如果在本地调试,记得关闭开发者工具里的 Disable cache,否则浏览器会绕过本地缓存,影响对 CDN 命中情况的判断。
七、CDN 缓存与浏览器缓存的关系
CDN 缓存不是孤立的一层。一次请求背后通常有三级缓存:浏览器本地缓存 → CDN 边缘缓存 → 源站。理解 HTTP 缓存(强缓存 / 协商缓存),才能搞清"为什么改了代码用户还是看到旧页面"。
7.1 强缓存 vs 协商缓存
| 对比项 | 强缓存 | 协商缓存 |
|---|---|---|
| 相关头字段 | Cache-Control: max-age / Expires |
ETag + If-None-Match;Last-Modified + If-Modified-Since |
| 是否发请求 | 不发,直接用本地副本 | 发请求到服务器(CDN/源站)确认 |
| 返回结果 | 200(from memory / disk cache) | 未变化返回 304;变化返回 200 + 新内容 |
| 优先级 | 命中则不再走协商缓存 | 强缓存失效后才走 |
| 典型用法 | 带哈希文件名的静态资源,设长 TTL | HTML、配置接口等要求及时更新的资源 |
7.2 三级缓存判断流程
#mermaid-svg-zosSYKLc3ExyCzI7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zosSYKLc3ExyCzI7 .error-icon{fill:#552222;}#mermaid-svg-zosSYKLc3ExyCzI7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zosSYKLc3ExyCzI7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zosSYKLc3ExyCzI7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zosSYKLc3ExyCzI7 .marker.cross{stroke:#333333;}#mermaid-svg-zosSYKLc3ExyCzI7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zosSYKLc3ExyCzI7 p{margin:0;}#mermaid-svg-zosSYKLc3ExyCzI7 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster-label text{fill:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster-label span{color:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster-label span p{background-color:transparent;}#mermaid-svg-zosSYKLc3ExyCzI7 .label text,#mermaid-svg-zosSYKLc3ExyCzI7 span{fill:#333;color:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 .node rect,#mermaid-svg-zosSYKLc3ExyCzI7 .node circle,#mermaid-svg-zosSYKLc3ExyCzI7 .node ellipse,#mermaid-svg-zosSYKLc3ExyCzI7 .node polygon,#mermaid-svg-zosSYKLc3ExyCzI7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zosSYKLc3ExyCzI7 .rough-node .label text,#mermaid-svg-zosSYKLc3ExyCzI7 .node .label text,#mermaid-svg-zosSYKLc3ExyCzI7 .image-shape .label,#mermaid-svg-zosSYKLc3ExyCzI7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-zosSYKLc3ExyCzI7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zosSYKLc3ExyCzI7 .rough-node .label,#mermaid-svg-zosSYKLc3ExyCzI7 .node .label,#mermaid-svg-zosSYKLc3ExyCzI7 .image-shape .label,#mermaid-svg-zosSYKLc3ExyCzI7 .icon-shape .label{text-align:center;}#mermaid-svg-zosSYKLc3ExyCzI7 .node.clickable{cursor:pointer;}#mermaid-svg-zosSYKLc3ExyCzI7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zosSYKLc3ExyCzI7 .arrowheadPath{fill:#333333;}#mermaid-svg-zosSYKLc3ExyCzI7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zosSYKLc3ExyCzI7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zosSYKLc3ExyCzI7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zosSYKLc3ExyCzI7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zosSYKLc3ExyCzI7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zosSYKLc3ExyCzI7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster text{fill:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 .cluster span{color:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zosSYKLc3ExyCzI7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zosSYKLc3ExyCzI7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-zosSYKLc3ExyCzI7 .icon-shape,#mermaid-svg-zosSYKLc3ExyCzI7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zosSYKLc3ExyCzI7 .icon-shape p,#mermaid-svg-zosSYKLc3ExyCzI7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zosSYKLc3ExyCzI7 .icon-shape .label rect,#mermaid-svg-zosSYKLc3ExyCzI7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zosSYKLc3ExyCzI7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zosSYKLc3ExyCzI7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zosSYKLc3ExyCzI7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否 / 过期
没变
变了
浏览器发起请求
浏览器本地缓存
仍在有效期内?
直接使用本地缓存
不发网络请求
CDN 边缘节点
缓存有效?
CDN 返回 200
X-Cache: HIT
CDN 回源
携带 If-None-Match 等协商头
源站内容
变化了吗?
源站返回 304
各级缓存继续用旧内容
源站返回 200 + 新内容
CDN、浏览器逐级更新缓存
7.3 关键结论
- HTML 通常不能用长强缓存 :要保证用户拿到最新入口,HTML 一般用
Cache-Control: no-cache(每次协商)或短 TTL。 - 带哈希的静态资源放心长缓存:文件内容变 → 文件名变 → URL 变,天然绕开旧缓存。
- CDN 缓存与浏览器缓存相互独立:浏览器缓存过期不代表 CDN 缓存过期,排查"更新不生效"时两层都要看。
八、防盗链与 URL 鉴权
CDN 按流量/带宽计费,资源被外站盗用会直接烧钱,所以防盗链是 CDN 的必备能力。
8.1 常见防护方式对比
| 方式 | 原理 | 优点 | 局限 |
|---|---|---|---|
| Referer 防盗链 | 校验请求头 Referer,配置白/黑名单 |
配置简单,控制台即可开启 | Referer 可伪造;需考虑空 Referer 的处理 |
| URL 鉴权(时间戳+签名) | URL 附带签名和过期时间,节点校验通过才返回 | 安全性高,链接有时效 | 需要服务端生成签名 URL |
| IP 黑白名单 | 按来源 IP 放行或拦截 | 简单直接 | 公网用户 IP 不固定,更适合封禁场景 |
| UA 防盗链 | 校验 User-Agent,拦截爬虫/下载工具 |
能挡住常见恶意 UA | UA 容易伪造 |
| HLS 加密 / DRM | 视频内容加密 + 授权播放 | 防盗效果最强 | 实现复杂,有额外成本 |
8.2 URL 鉴权的签名思路
text
原始 URL: https://cdn.example.com/video.mp4
签名 URL: https://cdn.example.com/video.mp4?auth_key=时间戳-rand-uid-md5哈希
校验逻辑: 节点用相同密钥重算哈希,比对一致且未过期才返回内容
要点:签名 URL 自带过期时间,防盗效果远好于 Referer;视频、下载类资源建议必配。
九、回源细节与常见坑
9.1 回源 HOST
CDN 节点回源时,请求头里的 Host 不一定等于加速域名。
- 默认回源 Host = 加速域名。
- 如果源站是 Nginx 虚拟主机 / 网关按 Host 分流,回源 Host 配错会 404 或打到默认站点。
9.2 回源协议
- HTTP 回源:性能好,但内容明文传输。
- HTTPS 回源:安全,但源站要承担 TLS 计算压力。
- 协议跟随:用户用什么协议访问,节点就用什么协议回源,最常用。
9.3 Range 回源(分片缓存)
视频、安装包等大文件,CDN 会按 HTTP Range 分片拉取和缓存:
- 用户拖动进度条时,只拉取对应分片,不用下载整个文件。
- 源站需支持 Range 请求,否则大文件回源效率极差。
9.4 获取用户真实 IP
经过 CDN 后,源站看到的连接 IP 是节点 IP,真实用户 IP 在透传头里:
X-Forwarded-For:客户端与中间代理 IP 列表,第一个通常是真实用户 IP。X-Real-IP:部分厂商提供的单值真实 IP 头。
安全提示:
X-Forwarded-For可被伪造,服务端应只信任 CDN 节点追加的部分,或使用厂商的可信头(如 Cloudflare 的CF-Connecting-IP)。
十、命中率、监控与计费
10.1 命中率怎么算
- 请求命中率 = 命中缓存的请求数 / 总请求数
- 字节命中率 = 命中缓存返回的字节数 / 总返回字节数
字节命中率更能反映带宽成本的节省:大文件(视频、安装包)只要命中,省的都是实打实的带宽。
10.2 关键监控指标
| 指标 | 关注点 |
|---|---|
| 命中率 / 回源率 | 命中率低说明缓存规则或缓存键有问题 |
| 带宽 / 流量 | 判断费用、发现异常刷量 |
| 状态码分布 | 5xx 突增往往意味着源站故障被放大 |
| 回源带宽 | 回源打满源站带宽,会导致源站自身不可用 |
| 首字节时间(TTFB) | 反映节点响应与回源链路质量 |
10.3 常见计费方式
| 计费方式 | 说明 | 适合场景 |
|---|---|---|
| 按流量 | 每 GB 计费,单价稍高 | 流量平稳的中小站点 |
| 按带宽峰值 | 按当日带宽峰值(Mbps)计费 | 流量集中在高峰突增的场景 |
| 月度 95 峰值带宽 | 去掉当月最高 5% 的峰值后计费 | 大流量、波动大的业务 |
| 按请求数 | 部分厂商对 HTTPS 请求数额外收费 | HTTPS 高并发场景 |
十一、接入 CDN 的标准流程
#mermaid-svg-faARyKM9JfCgvEZd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-faARyKM9JfCgvEZd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-faARyKM9JfCgvEZd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-faARyKM9JfCgvEZd .error-icon{fill:#552222;}#mermaid-svg-faARyKM9JfCgvEZd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-faARyKM9JfCgvEZd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-faARyKM9JfCgvEZd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-faARyKM9JfCgvEZd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-faARyKM9JfCgvEZd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-faARyKM9JfCgvEZd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-faARyKM9JfCgvEZd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-faARyKM9JfCgvEZd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-faARyKM9JfCgvEZd .marker.cross{stroke:#333333;}#mermaid-svg-faARyKM9JfCgvEZd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-faARyKM9JfCgvEZd p{margin:0;}#mermaid-svg-faARyKM9JfCgvEZd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-faARyKM9JfCgvEZd .cluster-label text{fill:#333;}#mermaid-svg-faARyKM9JfCgvEZd .cluster-label span{color:#333;}#mermaid-svg-faARyKM9JfCgvEZd .cluster-label span p{background-color:transparent;}#mermaid-svg-faARyKM9JfCgvEZd .label text,#mermaid-svg-faARyKM9JfCgvEZd span{fill:#333;color:#333;}#mermaid-svg-faARyKM9JfCgvEZd .node rect,#mermaid-svg-faARyKM9JfCgvEZd .node circle,#mermaid-svg-faARyKM9JfCgvEZd .node ellipse,#mermaid-svg-faARyKM9JfCgvEZd .node polygon,#mermaid-svg-faARyKM9JfCgvEZd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-faARyKM9JfCgvEZd .rough-node .label text,#mermaid-svg-faARyKM9JfCgvEZd .node .label text,#mermaid-svg-faARyKM9JfCgvEZd .image-shape .label,#mermaid-svg-faARyKM9JfCgvEZd .icon-shape .label{text-anchor:middle;}#mermaid-svg-faARyKM9JfCgvEZd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-faARyKM9JfCgvEZd .rough-node .label,#mermaid-svg-faARyKM9JfCgvEZd .node .label,#mermaid-svg-faARyKM9JfCgvEZd .image-shape .label,#mermaid-svg-faARyKM9JfCgvEZd .icon-shape .label{text-align:center;}#mermaid-svg-faARyKM9JfCgvEZd .node.clickable{cursor:pointer;}#mermaid-svg-faARyKM9JfCgvEZd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-faARyKM9JfCgvEZd .arrowheadPath{fill:#333333;}#mermaid-svg-faARyKM9JfCgvEZd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-faARyKM9JfCgvEZd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-faARyKM9JfCgvEZd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-faARyKM9JfCgvEZd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-faARyKM9JfCgvEZd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-faARyKM9JfCgvEZd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-faARyKM9JfCgvEZd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-faARyKM9JfCgvEZd .cluster text{fill:#333;}#mermaid-svg-faARyKM9JfCgvEZd .cluster span{color:#333;}#mermaid-svg-faARyKM9JfCgvEZd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-faARyKM9JfCgvEZd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-faARyKM9JfCgvEZd rect.text{fill:none;stroke-width:0;}#mermaid-svg-faARyKM9JfCgvEZd .icon-shape,#mermaid-svg-faARyKM9JfCgvEZd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-faARyKM9JfCgvEZd .icon-shape p,#mermaid-svg-faARyKM9JfCgvEZd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-faARyKM9JfCgvEZd .icon-shape .label rect,#mermaid-svg-faARyKM9JfCgvEZd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-faARyKM9JfCgvEZd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-faARyKM9JfCgvEZd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-faARyKM9JfCgvEZd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 添加加速域名
2. 配置源站
IP 或源站域名
3. 配置缓存规则
TTL / 缓存键 / 防盗链
4. 获取 CNAME 记录
5. 到 DNS 服务商
添加 CNAME 解析
6. 验证生效
nslookup + X-Cache
验证要点:
nslookup/dig确认域名已 CNAME 到厂商调度域名。- 请求静态资源,确认响应头出现
X-Cache: HIT。 - 观察监控面板的命中率是否逐步上升。
十二、常见问题排查(FAQ)
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 缓存命中率低 | URL 带随机参数、TTL 太短、缓存键配置不当 | 统一/忽略无关参数、调长 TTL、规范缓存键 |
| 更新后用户仍看到旧内容 | 各节点旧缓存未失效、浏览器强缓存未过期 | 文件名带哈希 + 发布后刷新;HTML 用协商缓存 |
| 接入 CDN 后源站 404 | 回源 Host 配置错误 | 检查回源 Host 与源站虚拟主机配置 |
| 刷新后仍是旧内容 | 刷新任务未生效、浏览器本地缓存 | 刷新一般几分钟内生效,浏览器强刷(Ctrl+F5) |
| 跨域请求报 CORS 错误 | CDN 未透传 Access-Control-Allow-Origin 等头 |
源站正确配置 CORS;多来源时加 Vary: Origin,避免缓存串响应头 |
| 源站看到的全是节点 IP | 未解析透传头 | 从 X-Forwarded-For 等可信头取真实 IP |
| 账单突增 | 资源被盗刷、热门大文件被外站盗链 | 配置防盗链 / URL 鉴权,设置用量封顶与告警 |