CDN 缓存完全指南:从一次"发了新版用户还看旧的、源站一挂整站全白"看懂 CDN 的正确用法

2022 年我做一个内容网站要给静态资源接入 CDN 加速。第一版我做得很省事把域名 CNAME 到 CDN 厂商配一个回源地址指向我的源站完事。本地和小流量下测了测真不错图片 JS CSS 加载肉眼可见地快了。我心里很踏实CDN 嘛把域名 CNAME 过去静态资源自动就走 CDN 加速了不就行了。可等这个网站真正上线扛起真实的用户流量一串问题冒了出来。第一种最先把我打懵我发布了一版新代码改了 CSS 和 JS 可有的用户看到的是新版有的还是旧版页面时新时旧。第二种月底一看 CDN 账单回源流量高得吓人大量请求根本没命中 CDN 缓存全回源砸到了我的源站上。第三种最致命有一次源站宕机了几分钟本以为有 CDN 顶着不要紧结果整个网站直接全白。第四种最隐蔽有个带用户登录态的个性化页面被 CDN 缓存了下来于是 A 用户刷出来的页面里赫然是 B 用户的昵称和数据。我盯着这一连串问题想了很久才彻底想明白第一版错在我以为接入 CDN 就是把域名 CNAME 过去静态资源自动就快了。可它不是。CDN 的本质是一张遍布各地的分布式缓存网它真正的核心从来不是机房离用户近而是缓存策略什么东西能缓存什么绝不能缓存用什么当缓存键缓存多久内容更新了怎么刷新源站挂了怎么兜底。这每一件都是你必须亲手设计的 CDN 不会替你想。真正用好 CDN 核心不是 CNAME 过去就完事而是理解 CDN 是缓存用 Cache-Control 精确指挥它把缓存键设计干净用内容指纹做刷新给源站做好兜底。本文从头梳理为什么 CNAME 过去就行是错的 Cache-Control 怎么指挥缓存缓存键怎么设计缓存怎么刷新源站怎么兜底以及动静分离私有内容命中率监控这些把 CDN 真正用对要避开的坑。

2022 年我做一个内容网站,要给静态资源接入 CDN 加速。第一版我做得很省事:把域名 CNAME 到 CDN 厂商,CDN 配一个回源地址指向我的源站,完事。本地和小流量下测了测——真不错:图片、JS、CSS 加载肉眼可见地快了。我心里很踏实:"CDN 嘛,把域名 CNAME 过去,静态资源自动就走 CDN 加速了,不就行了。"可等这个网站真正上线、扛起真实的用户流量,一串问题冒了出来。第一种最先把我打懵:我发布了一版新代码,改了 CSS 和 JS,可有的用户看到的是新版、有的还是旧版,页面时新时旧,我自己刷新十次有八次还是老样子。第二种:月底一看 CDN 账单,回源流量高得吓人——大量请求根本没命中 CDN 缓存,全回源砸到了我的源站上,CDN 几乎没起到加速作用。第三种最致命:有一次源站宕机了几分钟,本以为有 CDN 顶着不要紧,结果整个网站直接全白——CDN 缓存一过期就回源,源站一挂,它什么也返回不了。第四种最隐蔽:有个带用户登录态的个性化页面,被 CDN缓存了下来,于是A 用户刷出来的页面里,赫然是 B 用户的昵称和数据。我盯着这一连串问题想了很久才彻底想明白,第一版错在一个根本的认知上:我以为"接入 CDN,就是把域名 CNAME 过去,静态资源自动就快了"。这句话把 CDN 当成了一根"接上就生效"的加速电源。可它不是CDN 的本质是一张遍布各地的分布式缓存网,它真正的核心从来不是"机房离用户近",而是"缓存策略"——什么东西能缓存、什么绝不能缓存、用什么当缓存键、缓存多久、内容更新了怎么刷新、源站挂了怎么兜底。这每一件,都是你必须亲手设计的,CDN 不会替你想。真正用好 CDN,核心不是"CNAME 过去就完事",而是理解 CDN 是缓存、用 Cache-Control 精确指挥它、把缓存键设计干净、用内容指纹做刷新、给源站做好兜底。这篇文章就把 CDN 缓存梳理一遍:为什么"CNAME 过去就行"是错的、Cache-Control 怎么指挥缓存、缓存键怎么设计、缓存怎么刷新、源站怎么兜底,以及动静分离、私有内容、命中率监控这些把 CDN 真正用对要避开的坑。

问题背景

先把那串问题的现象和我的误判讲清楚,后面所有的设计都是冲着纠正这个误判去的。

现象:把域名 CNAME 到 CDN、配好回源之后,上线冒出一串问题:发布新版本后,用户看到的页面时新时旧,旧缓存死活不更新;CDN 命中率极低,大量请求回源,账单暴涨而加速无感;源站一宕机,整站跟着全白,CDN 没能兜住;带登录态的个性化页面被缓存,用户之间串了数据。

我当时的错误认知:"接入 CDN,就是把域名 CNAME 过去,静态资源自动就走加速了。"

真相:CDN(Content Delivery Network,内容分发网络)的本质,是一张由分布在世界各地的边缘节点组成的缓存网。用户的请求,不再直奔你那台源站,而是先落到离他最近的边缘节点。这个节点缓存命中,就直接把内容返回——又快又不碰你的源站;缓存未命中,它才会回源,从你的源站取一份,边返回边按规矩缓存下来。所以 CDN 快不快、省不省,全押在"命中率"这三个字上。而命中率,不是 CNAME 一配就自动很高的:它取决于你有没有告诉 CDN 哪些能缓存、缓存多久(Cache-Control),取决于你缓存键设计得干不干净(无关参数会把缓存打成碎片),取决于你内容更新后有没有正确地让旧缓存失效。CDN 只是忠实地执行你给的缓存规则——你不给规则,或者给错了,它就把你的网站加速成一团乱麻。

要把 CDN 缓存用对,需要几块认知:

  • 为什么"CNAME 过去就行"是错的——CDN 是缓存,缓存要你来设计;
  • Cache-Control——源站用这个响应头,精确指挥 CDN 怎么缓存;
  • 缓存键——别让 utm 之类的无关参数把缓存打成碎片;
  • 缓存刷新——用内容指纹让更新自动生效,用 purge 兜底;
  • 源站兜底、动静分离、命中率监控这些工程坑怎么处理。

一、为什么"CNAME 过去就完事了"是错的

先把这件最根本的事钉死:CDN 不是一个"接上就自动加速一切"的黑盒。它是一个极其听话、又极其没有主见的缓存执行器——你的源站在每一个响应里怎么说,它就怎么做。你说"这个能缓存一年",它就存一年,哪怕这一年里你改了八遍;你什么都不说,它就只能按一套默认的、往往并不适合你的策略去猜。你接入 CDN 却不设计缓存策略,等于雇了一个能力极强、却完全不给他下指令的员工——他不会偷懒,但他做的每一件事,都未必是你想要的。

下面这段配置,就是我那个"CNAME 过去就完事"的第一版源站:

# 反面教材:接入 CDN 后,源站对所有响应一视同仁
server {
    listen 80;
    server_name static.example.com;
    root /var/www/site;

    location / {
        try_files $uri $uri/ /index.html;
    }
    # 破绽一:没有任何 Cache-Control,CDN 只能用默认策略瞎猜。
    # 破绽二:HTML、JS、图片、接口全走同一套,该长缓存的没长缓存、
    #         该绝不缓存的反被缓存。
    # 破绽三:源站一挂,CDN 缓存过期后无任何兜底,整站跟着挂。
}

这段配置在本地和小流量下测试时表现不错,因为那时回源压力小、内容也没更新过,CDN 的默认策略恰好没惹麻烦。它的问题不在配置本身,而在一个被忽略的前提:它默认"CDN 会自己想清楚每样东西该怎么缓存"。可 CDN 不会替你思考,它只会执行。于是那串问题就有了解释:页面时新时旧,是因为源站没说 HTML 不能长缓存,CDN 就把旧 HTML 存住了;命中率低,是因为源站没说哪些能缓存,CDN 默认不敢缓存,只能回源;源站一挂整站全白,是因为没有任何"源站故障时拿旧缓存兜底"的配置。问题的根子清楚了:用好 CDN 的工程量,全在"承认 CDN 只是个缓存执行器、缓存规则必须你来定"之后——你不给它规则,它就把你的网站加速成一团乱。先从最关键的那条规则——Cache-Control 说起。

二、Cache-Control:源站如何精确指挥 CDN

源站和 CDN 之间,沟通的语言就是 HTTP 响应头,而其中最核心的一个,是 Cache-Control。一份资源能不能被缓存、能被谁缓存、缓存多久、过期之后怎么办,全由源站在响应里的这个头说了算:

# 资源能不能被缓存、缓存多久,全由源站的这个响应头说了算

Cache-Control: public, max-age=31536000, immutable
#   public     —— 允许 CDN 这类"共享缓存"存它
#   max-age    —— 缓存有效期(秒),这里是一年
#   immutable  —— 告诉浏览器:有效期内别再来问,内容绝不会变

Cache-Control: private, no-store
#   private    —— 只允许用户浏览器存,CDN 共享缓存绝不能存
#   no-store   —— 干脆谁都别存(用于带用户隐私的响应)

Cache-Control: public, max-age=0, s-maxage=60
#   s-maxage   —— 专门给 CDN 的有效期,优先级高于 max-age
#               这条:浏览器每次校验,但 CDN 可缓存 60 秒

这里有个极易忽略的关键:s-maxage专门写给 CDN 这类共享缓存看的,它能和 max-age 取不同的值——这让你可以让 CDN 缓存得久一点、让浏览器缓存得短一点。源站要做的,就是主动、明确地给每一类资源派发不同的 Cache-Control,别再一视同仁:

# 源站要主动、明确地告诉 CDN:每一类资源该怎么缓存
map $uri $cache_policy {
    default                    "public, max-age=0, s-maxage=60";
    ~*\.html$                  "no-cache";                  # HTML 每次校验
    ~*\-[0-9a-f]{8}\.(js|css)$ "public, max-age=31536000, immutable";
    ~*^/api/                   "private, no-store";         # 接口绝不缓存
}

server {
    location / {
        add_header Cache-Control $cache_policy;
        proxy_pass http://origin;
    }
}

下面这张图,把一次请求穿过 CDN 的完整流程串起来:

这里的认知要点是:Cache-Control 是你和 CDN 之间唯一的、也是最重要的契约。CDN 的每一个缓存行为,都是在严格执行这份契约。你想让 CDN 怎么做,就必须在源站的响应头里把话说清楚、说准确——说错了,CDN 会一丝不苟地把你的错误放大到每一个边缘节点。不过,光说清楚"缓存多久"还不够。CDN 还得知道"两个请求到底算不算同一个东西"——这就是缓存键。

三、缓存键设计:别让无关参数把缓存打成碎片

CDN 判断"这个请求要的东西,我缓存里有没有",靠的是一个叫缓存键(cache key)的东西。默认情况下,缓存键由完整的 URL 构成,包括后面所有的查询参数。问题就出在这:很多查询参数,根本不影响返回的内容——比如营销链接里的 utm_sourceutm_campaign,比如各种统计追踪参数。可只要它们出现在 URL 里,CDN 就认为这是一个全新的、没见过的请求,于是每一个带不同 utm 参数的链接,都在 CDN 上存了一份一模一样的缓存——缓存被打成了无数互不复用的碎片,命中率自然低得可怜:

# 缓存键:决定"两个请求算不算同一个东西"
# 默认缓存键含全部查询参数 —— utm 这类无关参数会让缓存碎片化

location ~* \.(js|css|png|jpg|webp)$ {
    proxy_cache cdn_cache;
    # 关键:缓存键只用 协议+主机+路径,不含 $args
    # 这样 a.js?utm_source=x 和 a.js?utm_source=y 命中同一份缓存
    proxy_cache_key "$scheme$host$uri";
    proxy_cache_valid 200 30d;
    add_header Cache-Control "public, max-age=2592000";
    proxy_pass http://origin;
}

但要小心另一个极端:有些查询参数是真正影响内容的——比如 ?v=2 这种版本号、比如分页的 ?page=3。这些不能丢,丢了就会把不同内容混成一份缓存。所以正确的做法不是"全部参数都进缓存键"或"全部参数都不进",而是用一个白名单,精确地只保留那些真正影响内容的参数:

# 有些参数确实影响内容(如 ?v=2),不能全丢 —— 用白名单只保留它们
map $args $cache_args {
    default                  "";          # 默认:无关参数一律不进缓存键
    ~*(^|&)v=([0-9]+)        "v=$2";       # 只有 v= 这个版本参数被保留
}

location ~* \.(js|css)$ {
    proxy_cache cdn_cache;
    # 缓存键 = URI + 白名单里的参数,无关参数被彻底排除
    proxy_cache_key "$scheme$host$uri?$cache_args";
    proxy_pass http://origin;
}

这里的认知要点是:缓存键定义了"什么算同一个东西"。它放得太宽(含上无关参数),同一份资源会裂成无数碎片,命中率崩塌;它放得太窄(把影响内容的参数也丢了),不同的内容会被混成一份,用户拿到错的东西。缓存键设计的全部功夫,就是用一份白名单,精确地框定"真正影响内容的因素"——不多一个,不少一个。缓存键干净了,命中率上来了,但还有一个绕不开的难题:内容更新了,那些缓存怎么失效?

四、缓存刷新:用内容指纹,而不是靠运气

开头第一个问题——"发了新版,用户还看到旧的"——根子在于:CDN 把旧资源缓存住了,而我没有任何机制让它失效。解决缓存刷新,有两种思路,优先用第一种。第一种,也是最优雅的——内容指纹(content hash):让构建工具把文件内容的 hash 写进文件名。这样内容只要一变,文件名就跟着变,变成一个 CDN 从没见过的全新 URL,自然就是一次必然的未命中、必然回源取新的——根本不需要"刷新"旧缓存,旧 URL 自然没人再访问了:

// 内容指纹:让文件名带上内容 hash —— 内容一变,文件名就变
// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        // 产物形如 app.4f3a9c2b.js —— hash 由文件内容算出
        // 改一个字符,hash 就变,文件名就变,CDN 视作全新资源
        entryFileNames: 'assets/[name].[hash].js',
        chunkFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]',
      },
    },
  },
}

有了内容指纹,带 hash 的资源就能放心地长缓存一年;但引用这些资源的 HTML 入口文件本身,绝不能长缓存——它是用户进入新版本的唯一入口,必须每次都校验新鲜度。第二种思路,是主动刷新(purge):有些内容(比如一篇被编辑过的文章)URL 不会变,这时就得在内容发布后,主动调 CDN 的 API,命令它把指定 URL 的旧缓存清掉:

import requests


def purge_cdn(urls, zone_id, api_token):
    """内容发布后,主动通知 CDN 把这些 URL 的旧缓存清掉。"""
    resp = requests.post(
        f"https://api.cdn-vendor.com/zones/{zone_id}/purge_cache",
        headers={"Authorization": f"Bearer {api_token}"},
        json={"files": urls},
        timeout=10,
    )
    result = resp.json()
    if not result.get("success"):
        # 刷新失败必须重试或告警 —— 否则用户会一直停在旧内容上
        raise RuntimeError(f"CDN 缓存刷新失败: {result}")
    return result

这里的认知要点是:缓存刷新有"主动"和"被动"两条路,而最好的刷新是"不需要刷新"。内容指纹的精妙,正在于它把"更新内容"变成了"换一个全新的 URL"——旧缓存不必清理,它只是自然地再也无人问津。能用指纹解决的(静态资源),就别去依赖 purge;只有 URL 必须保持稳定的内容,才动用主动 purge,并且要管好它的失败重试。缓存的"生"与"灭"都讲清楚了,但还有一个致命问题没解决:CDN 回源时,源站要是挂了怎么办?

五、源站兜底:别让 CDN 反而成了放大故障的单点

开头第三个问题——"源站一挂,整站全白"——暴露了一个反直觉的事实:接了 CDN,如果不做兜底,源站故障的影响面反而可能更大。因为所有边缘节点的缓存一旦过期,会同时回源,而源站此刻是挂的——CDN 不仅没顶住,还把一个源站故障,同步广播成了全网故障。第一道兜底:源站故障时,允许 CDN 继续返回过期的旧缓存。旧内容,总好过一个白屏:

# 回源保护:源站故障/超时/过载时,宁可返回过期缓存,也别把错误甩给用户
location / {
    proxy_cache cdn_cache;
    proxy_cache_valid 200 5m;

    # 源站出错、超时、返回 5xx、或缓存正在更新时,继续用旧缓存兜底
    proxy_cache_use_stale error timeout updating
                          http_500 http_502 http_503 http_504;

    # 同一资源同时多个请求要回源时,只放一个过去,其余等它的结果
    # —— 防止缓存一过期,成千请求同时砸向源站(缓存击穿)
    proxy_cache_lock on;

    proxy_pass http://origin;
}

对应到 Cache-Control,源站还可以用 stale-if-errorstale-while-revalidate 两个指令,把这个意图明确写进契约。第二道兜底:源站本身要有冗余,别只有一台。主源站连不上,CDN 回源时要能自动切到备用源站,并且回源超时要设得短,别让请求在一台挂掉的源站上干耗:

# 源站要有冗余:主源站挂了,回源自动切到备用源站
upstream origin {
    server origin-a.internal:80 max_fails=3 fail_timeout=30s;
    server origin-b.internal:80 max_fails=3 fail_timeout=30s backup;
}

server {
    location / {
        proxy_pass http://origin;
        proxy_connect_timeout 2s;   # 连不上源站,2 秒就放弃,别让请求干等
        # 一台源站出错就立刻换下一台
        proxy_next_upstream error timeout http_502 http_503 http_504;
    }
}

这里的认知要点是:CDN 是一层缓存,缓存的本意是"挡在源站前面替它扛"——可一旦缓存过期、又没有任何兜底,这层缓存就会瞬间从"防护盾"变成"故障放大器",把一次源站抖动同步成一场全网雪崩。源站兜底的核心,就是给缓存补上"过期之后、源站又恰好不可用"这个最坏时刻的预案:宁可端出一份过期的旧饭,也绝不端出一个空盘子。主链路的设计讲完了,最后是几个真正上规模后才会撞见的工程坑。

六、工程坑:动静分离、私有内容与命中率监控

五块设计之外,还有几个工程坑,不处理就会让 CDN 要么帮倒忙、要么泄露数据、要么你根本不知道它有没有在干活坑 1:动静要分离,别把动态接口也丢给 CDN 缓存。CDN 擅长的是静态资源——图片、JS、CSS,这些"对所有人都一样"的内容。而动态接口的响应因人而异、时刻在变,一旦被缓存,轻则数据陈旧,重则就是开头第四个问题——A 用户看到了 B 用户的数据。所以动态内容,源站必须用 Cache-Control: private, no-store 明确禁止共享缓存坑 2:私有内容除了 no-store,还要善用 Vary如果一个响应会因为某个请求头不同而不同(比如 Accept-Encoding 决定返回 gzip 还是 br,Accept-Language 决定语言),就必须用 Vary告诉 CDN"这个响应跟哪些请求头有关",否则 CDN 会把给 A 语言的缓存,错发给 B 语言的用户。但 Vary 也别滥用——Vary: User-Agent 这种几乎人人不同的头,会让缓存碎成齑粉。坑 3:命中率必须监控,不能凭感觉。"我觉得 CDN 在加速"是没有意义的。CDN 的访问日志里,每条请求都有一个缓存状态字段(HIT / MISS / EXPIRED / STALE),要定期解析它、算出命中率:

import re
from collections import Counter


def cdn_hit_rate(log_path):
    """解析 CDN 访问日志,算缓存命中率 —— 命中率是 CDN 值不值的核心指标。"""
    stats = Counter()
    # 日志每行都有一个缓存状态字段:HIT / MISS / EXPIRED / STALE
    pattern = re.compile(r'cache_status:(\w+)')
    with open(log_path, encoding='utf-8') as f:
        for line in f:
            m = pattern.search(line)
            if m:
                stats[m.group(1)] += 1

    total = sum(stats.values())
    hit = stats.get('HIT', 0) + stats.get('STALE', 0)
    rate = hit / total if total else 0.0
    # 命中率长期低于 90%,几乎一定是缓存键设计或 Cache-Control 出了问题
    return {'hit_rate': round(rate, 4), 'detail': dict(stats)}

坑 4:大文件刷新前先想清楚,别一键全清。很多人遇到缓存问题就"刷新整个域名"——这会让所有边缘节点的缓存瞬间全部失效,紧接着海量请求同时回源,把源站直接打垮。刷新要尽量精确到具体 URL,实在要大面积刷,也该分批、错峰坑 5:TLS,在 HTTP 之上加一层 TLS 加密,防止中间人窃听和篡改。">HTTPS 证书和回源协议要对齐。CDN 接入后,用户到 CDNCDN 回源到源站两段独立的连接。如果用户用 HTTPS、而 CDN 回源用 HTTP,源站又有"HTTP 强制跳 HTTPS"的规则,就会撞上无限重定向。回源协议、Host 头、端口,都要和源站的预期对齐。

关键概念速查

概念 / 手段 说明
边缘节点 CDN 分布各地的缓存服务器,用户就近访问
回源 边缘节点缓存未命中时,向源站取内容的过程
命中率 请求被缓存直接满足的比例,CDN 价值的核心指标
Cache-Control 源站指挥缓存的响应头,决定能否缓存、缓存多久
s-maxage 专给 CDN 共享缓存的有效期,优先级高于 max-age
缓存键 判定"是否同一资源"的依据,无关参数会使其碎片化
内容指纹 文件名带内容 hash,内容一变 URL 就变,免去刷新
purge 主动调 API 让 CDN 清除指定 URL 的旧缓存
stale 兜底 源站故障时继续返回过期缓存,避免整站白屏
Vary 声明响应随哪些请求头变化,防止缓存错发

避坑清单

  1. CDN 是缓存执行器,只忠实执行你给的规则,不会替你思考。
  2. 源站要给每类资源派发明确的 Cache-Control,别一视同仁。
  3. HTML 入口绝不长缓存,带指纹的静态资源才放心长缓存。
  4. 用 s-maxage 单独控制 CDN 缓存时长,与浏览器分开。
  5. 缓存键要排除 utm 等无关参数,只用白名单留影响内容的参数。
  6. 缓存刷新优先用内容指纹,URL 稳定的内容才用主动 purge。
  7. 配 proxy_cache_use_stale,源站故障时用过期缓存兜底。
  8. 源站要有备用节点,回源超时设短,别在挂掉的源站上干耗。
  9. 动态接口用 private no-store 明确禁缓存,防止用户串数据。
  10. 持续监控命中率,低于九成就排查缓存键和 Cache-Control。

总结

回头看那串"页面时新时旧、命中率极低、源站一挂全白、用户串了数据"的问题,以及我后来在 CDN 上接连踩的坑,最该记住的不是某一个配置项,而是我动手前那个想当然的判断——"接入 CDN,就是把域名 CNAME 过去,静态资源自动就快了"。这句话错在它把 CDN 当成了一个"接通即生效"的加速开关。我以为把域名指过去,剩下的 CDN 会自己安排妥当。可我忽略了一件事:CDN 是一张缓存网,而缓存这件事,从来没有"自动就对"的默认答案什么能缓存、缓存多久、怎么算同一个东西、更新了怎么失效、源站挂了怎么办——这每一个问题,CDN 都在等你给答案。你不答,它就用一套谁都不适合的默认值替你答,然后把这个答案,一丝不苟地复制到全世界每一个边缘节点。

所以用好 CDN,真正的工程量不在"把 CNAME 记录配好"那一步上。那一步,谁都会做。真正的工程量,在于你要承认"CDN 只是个忠实的缓存执行器,缓存策略得你亲手设计",并据此把每一类内容都想一遍:它是 HTML 还是带指纹的静态资源,你就给它派发不同的 Cache-Control;它的 URL 上挂着 utm 这种无关参数,你就把缓存键洗干净、只留白名单;它的内容会更新,你就用内容指纹让 URL 自己换代、用 purge 兜住 URL 稳定的那部分;它总有源站会抖动的那一刻,你就配好 stale 兜底和备用源站;它里面混着动态接口,你就用 no-store 把它们挡在缓存之外。这篇文章的几节,其实就是顺着这条线展开的:先想清楚"CNAME 过去就行"为什么错,再讲 Cache-Control 怎么指挥、缓存键怎么设计、缓存怎么刷新、源站怎么兜底,最后是动静分离、私有内容、命中率监控这几个把 CDN 用扎实的工程细节。

你会发现,CDN 这套缓存,和现实里"一家全国连锁店怎么备货"完全相通。总部的中央仓库,就是你的源站;遍布各城市的门店,就是边缘节点。一个不懂经营的老板会怎么做?他开了门店,却不给任何备货指令——门店不知道该囤什么、囤多久,顾客一上门,店员只能每一单都打电话回中央仓库现问现调(这就是缓存全不命中、全回源)。生意一火,中央仓库的电话被打爆,整个连锁跟着瘫痪(这就是源站被回源打垮)。而一个懂经营的老板怎么做?他给每类商品定好备货规则:畅销的标品,门店敞开了囤、囤足一整季(这就是带指纹的静态资源长缓存);当天的鲜货,门店只摆样品、卖一份补一份(这就是 HTML 走校验);顾客的私人订制,绝不摆上货架,只能由总部直发(这就是动态接口 no-store);就算中央仓库临时断供,门店货架上也总还有存货顶着(这就是 stale 兜底)。两家连锁,门店一样多,可前者天天在打电话救火,后者再大的客流也从容不迫——差别不在门店数量,只在那一套"什么货、囤多久、断供怎么办"的备货规则

最后想说,CDN 用没用对,差距永远不会在"本地一测、资源加载快了"时暴露——本地你没有真实流量、没有发过新版本、源站也一直好好的,CDN 那套粗糙的默认策略恰好没惹出乱子,你会觉得"CNAME 一配"已经是全部。它只在真实的、有持续流量、要不断发布更新、源站偶尔会抖动的线上环境里才显形。那时候它会用最伤体验的方式给你结账:做不好,你的用户会卡在永远刷不掉的旧版本里,你的账单会因为命中率太低而虚高,源站一抖整站跟着白屏,甚至把一个用户的数据缓存着发给了另一个用户;而做了,你的 CDN 会稳稳地替源站扛住绝大多数流量:静态资源该长缓存的长缓存,新版本一发布用户就自然换代,源站偶尔抖动也被旧缓存悄悄兜住,私有数据一丝都不会串。所以别等"用户抱怨页面死活不更新"找上门,在你把那条 CNAME 记录指过去的时候就该想清楚:我交给 CDN 的不是一个"接上就自动加速"的开关,而是一整套需要我逐类内容去设计的缓存规则——这每一类内容,该缓存多久、缓存键干不干净、更新怎么生效、源站挂了怎么办,这一道道规则,我是不是都替它定清楚了?这些问题有了答案,你接入的才不只是一个"看起来变快了"的 CDN,而是一张真正扛得住流量、经得起发布与故障的可靠加速网

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

RAG 检索重排序完全指南:从一次"知识库明明有答案、却死活检索不出来"看懂 Rerank

2026-5-22 1:46:32

技术教程

LLM 推理服务完全指南:从一次"GPU 利用率很低、并发一高就排长队还 OOM"看懂批处理与请求队列

2026-5-22 1:59:20

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索