土法炼钢兴趣小组的算法知识备份

【网络工程】CDN 缓存策略:TTL、Purge 与 stale-while-revalidate

文章导航

分类入口
network
标签入口
#cdn#cache#ttl#purge#stale-while-revalidate#cache-control

目录

CDN 的核心是缓存——缓存策略的好坏直接决定了命中率、源站负载和用户体验。但缓存策略的设计远比”设个 TTL”复杂:

这些问题没有”万能答案”——它们取决于你的内容特征、更新频率和一致性要求。本文从 Cache-Control 指令全景、TTL 设计方法论、Purge 机制到缓存优化实战,系统讲解 CDN 缓存策略的工程实践。

一、Cache-Control 指令全景

HTTP Cache-Control 头是缓存策略的基础。理解每个指令的精确语义是正确配置缓存的前提。

1.1 响应缓存指令

Cache-Control 响应指令速查:

可缓存性:
├── public       → 任何缓存都可以存储(包括共享缓存如 CDN)
├── private      → 只有浏览器可以缓存,CDN 不可以
├── no-store     → 完全不允许任何缓存存储
└── no-cache     → 可以存储,但每次使用前必须向源站验证

TTL 控制:
├── max-age=N        → 资源在浏览器和 CDN 中的最大缓存秒数
├── s-maxage=N       → 仅对共享缓存(CDN)生效,覆盖 max-age
├── max-stale=N      → 客户端愿意接受过期不超过 N 秒的内容
└── min-fresh=N      → 客户端要求资源至少还有 N 秒才过期

重新验证:
├── must-revalidate      → 过期后必须验证,不可使用 stale 内容
├── proxy-revalidate     → 仅对共享缓存的 must-revalidate
├── stale-while-revalidate=N → 过期后 N 秒内可返回旧内容,同时后台刷新
└── stale-if-error=N     → 源站出错时 N 秒内可返回旧内容

其他:
├── no-transform     → 禁止中间代理修改内容(如压缩、图片转换)
└── immutable        → 内容永不变化,不需要条件验证

1.2 关键指令的精确语义

no-cache vs no-store

no-cache:
- CDN 可以存储内容
- 每次客户端请求时,CDN 必须向源站发条件请求验证
- 如果内容没变(304),CDN 直接返回缓存
- 适用于:变化频率不确定但需要保证新鲜度的内容

no-store:
- 完全禁止存储
- 每次请求都必须完整回源
- 适用于:敏感数据(个人信息、支付数据)

常见误用:
Cache-Control: no-cache, no-store, must-revalidate
→ 同时写三个是多余的
→ no-store 已经足够禁止缓存
→ 但这种写法在实际中很常见,是"防御性编程"

max-age vs s-maxage

Cache-Control: max-age=60, s-maxage=3600

浏览器缓存:60 秒
CDN 缓存:3600 秒(1 小时)

为什么分开设置?
- 浏览器缓存过期后可以条件请求获取 304
- CDN 缓存可以服务大量用户,命中率更重要
- 浏览器 TTL 短 → 用户更快看到更新
- CDN TTL 长 → 减少回源,降低源站压力

immutable

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

含义:资源在 max-age 期间内容不会变化
效果:浏览器在有效期内不发送条件验证请求(304 请求也不发)
适用:带版本号/哈希的静态资源
  /static/app.a1b2c3d4.js
  /static/style.e5f6g7h8.css
  /images/logo.v2.png

不适用:
  /api/data(内容会变)
  /index.html(HTML 可能更新)

1.3 条件验证

当缓存过期时,CDN 可以向源站发条件请求,避免完整传输:

条件验证流程:

CDN 缓存过期 → 发条件请求给源站:
If-None-Match: "etag-value"
If-Modified-Since: Wed, 01 Jan 2025 00:00:00 GMT

源站响应:
├── 304 Not Modified → 内容没变,CDN 刷新 TTL
│   → 节省带宽(无 body 传输)
│   → 延迟 = 1 RTT
└── 200 OK(新内容)→ CDN 更新缓存
    → 完整传输新内容
    → 延迟 = 1 RTT + 传输时间

ETag vs Last-Modified

维度 ETag Last-Modified
精度 字节级 秒级
生成方式 哈希/版本号 文件修改时间
强/弱验证 支持弱 ETag(W/) 只能弱验证
推荐 优先使用 作为回退
# Nginx 配置 ETag 和 Last-Modified
location /static/ {
    etag on;                    # 默认开启
    if_modified_since exact;    # 精确匹配
    add_header Cache-Control "public, max-age=86400";
}

二、TTL 设计方法论

TTL 设置没有”最佳值”——它取决于内容特征、更新频率和一致性要求的平衡。

2.1 按内容类型设计 TTL

内容类型 推荐 TTL Cache-Control 理由
带哈希的 JS/CSS 1 年 public, max-age=31536000, immutable 文件名含哈希,内容永不变
图片/字体/视频 30 天 public, max-age=2592000 变化频率低
HTML 页面 5-60 分钟 public, max-age=300, s-maxage=3600 内容可能更新
API 数据 0-60 秒 public, s-maxage=60no-cache 需要较高新鲜度
用户个性化数据 0 private, no-store 不应缓存
搜索结果 5-60 秒 public, s-maxage=30 短 TTL 平衡新鲜度
实时数据(股价等) 0 no-cacheno-store 必须实时

2.2 TTL 过长 vs 过短的权衡

TTL 过长(如 HTML 设为 1 年):
├── 优点:命中率极高,源站几乎零负载
├── 缺点:内容更新后用户看不到
├── 风险:错误内容被长期缓存
└── 修复:需要 Purge + 用户清浏览器缓存

TTL 过短(如静态资源设为 1 分钟):
├── 优点:内容更新快速可见
├── 缺点:频繁回源,源站压力大
├── 风险:TTL 到期时的"回源风暴"
└── 浪费:大部分回源发现内容没变(304)

2.3 源站 Cache-Control 配置

# Nginx 源站的分类缓存配置

# 带哈希的静态资源:永久缓存
location ~* \.[0-9a-f]{8}\.(css|js)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

# 普通静态资源
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2|ttf)$ {
    add_header Cache-Control "public, max-age=2592000";
}

# HTML 页面
location ~* \.html$ {
    add_header Cache-Control "public, max-age=300, s-maxage=3600";
}

# API 接口
location /api/ {
    # 列表接口:短缓存
    location ~* /api/list {
        add_header Cache-Control "public, s-maxage=60";
    }
    
    # 用户数据:不缓存
    location ~* /api/user {
        add_header Cache-Control "private, no-store";
    }
    
    # 公开数据:适度缓存
    location ~* /api/public {
        add_header Cache-Control "public, s-maxage=300";
    }
}

2.4 版本化资源策略

最佳实践是将资源分为两类——版本化资源和入口文件:

版本化策略(推荐):

入口文件(HTML):
  /index.html → Cache-Control: no-cache
  → 每次访问都验证(但 304 很快)

版本化资源(JS/CSS/图片):
  /static/app.a1b2c3d4.js → max-age=31536000, immutable
  /static/style.e5f6g7h8.css → max-age=31536000, immutable
  → 永久缓存,文件名变化即为新版本

更新流程:
1. 构建新版本 → 生成新文件名(app.x9y8z7w6.js)
2. 部署新 HTML(引用新文件名)
3. 用户刷新页面 → 加载新 HTML → 引用新 JS/CSS
4. 新文件缓存一年
5. 旧文件自然过期(或被 CDN LRU 淘汰)

三、Purge 机制

当内容更新时,需要主动清除 CDN 中的旧缓存。Purge(清除)是 CDN 运营中最常用也最容易出问题的操作。

3.1 Purge 类型

Purge 类型:

1. 单 URL Purge
   清除一个精确的 URL
   PURGE https://www.example.com/article/123
   → 快速(秒级),精确
   → 适用于单个内容更新

2. 目录 Purge(Wildcard Purge)
   清除匹配模式的所有 URL
   PURGE https://www.example.com/static/*
   → 中等速度(10-30 秒)
   → 适用于批量更新

3. 全量 Purge(Purge All)
   清除整个站点的所有缓存
   → 最慢(1-5 分钟)
   → 回源风暴风险高
   → 只在紧急情况使用

4. Tag-based Purge(Surrogate Key)
   通过标签关联内容,按标签清除
   Surrogate-Key: product-123 category-shoes
   → 快速,语义化
   → 需要 CDN 支持(Fastly、Cloudflare)

3.2 各 CDN 的 Purge API

# Cloudflare Purge API
# 单 URL 清除
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"files":["https://www.example.com/article/123"]}'

# Tag 清除
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tags":["product-123"]}'

# 全量清除(危险!)
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
  -H "Authorization: Bearer API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"purge_everything":true}'
# CloudFront Invalidation
aws cloudfront create-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --paths "/article/123" "/static/*"

# 查看 Invalidation 状态
aws cloudfront get-invalidation \
  --distribution-id DISTRIBUTION_ID \
  --id INVALIDATION_ID

# 注意:
# - CloudFront 每月前 1000 次 Invalidation 免费
# - 超出后按 $0.005/path 收费
# - 通配符 /* 算一个 path
# Fastly Instant Purge(毫秒级)
# 单 URL
curl -X PURGE "https://www.example.com/article/123" \
  -H "Fastly-Key: API_KEY"

# Surrogate Key
curl -X POST "https://api.fastly.com/service/SERVICE_ID/purge/product-123" \
  -H "Fastly-Key: API_KEY"

# Fastly 的 Instant Purge 承诺 150ms 全球生效

3.3 Surrogate Key(Tag-based Purge)

Surrogate Key 是最灵活的 Purge 机制——在源站响应中标记内容的关联标签,Purge 时按标签清除所有关联内容:

场景:电商网站的商品页面

源站响应(商品 123 的页面):
Surrogate-Key: product-123 category-shoes brand-nike homepage-featured
Cache-Control: public, s-maxage=86400

场景 1:商品 123 价格变了
→ Purge tag: product-123
→ 清除所有包含 product-123 标签的页面

场景 2:Nike 品牌更换 Logo
→ Purge tag: brand-nike
→ 清除所有 Nike 商品页面

场景 3:首页推荐更新
→ Purge tag: homepage-featured
→ 清除所有被首页引用的页面

在源站实现 Surrogate Key:

# Django 示例
from django.http import JsonResponse

def product_detail(request, product_id):
    product = Product.objects.get(id=product_id)
    response = JsonResponse(product.to_dict())
    
    # 设置 Surrogate Key
    tags = [
        f"product-{product.id}",
        f"category-{product.category.slug}",
        f"brand-{product.brand.slug}",
    ]
    if product.is_featured:
        tags.append("homepage-featured")
    
    response['Surrogate-Key'] = ' '.join(tags)
    response['Cache-Control'] = 'public, s-maxage=86400'
    
    return response

3.5 Purge 自动化与 CI/CD 集成

在 CI/CD 流程中自动触发 Purge,确保部署后 CDN 内容及时更新:

#!/bin/bash
# deploy-and-purge.sh — 部署后自动 Purge CDN

set -e

# 1. 部署新版本到源站
echo "Deploying new version..."
rsync -avz ./build/ origin-server:/var/www/html/

# 2. 等待源站就绪
echo "Waiting for origin to be ready..."
until curl -sf http://origin-server/health > /dev/null; do
    sleep 1
done

# 3. 根据变更文件列表做精确 Purge
changed_files=$(git diff --name-only HEAD~1 HEAD -- public/)
purge_urls=""
for file in $changed_files; do
    url="https://www.example.com/${file#public/}"
    purge_urls="$purge_urls\"$url\","
done

# 去掉最后的逗号
purge_urls="${purge_urls%,}"

if [ -n "$purge_urls" ]; then
    echo "Purging ${#changed_files[@]} URLs..."
    curl -s -X POST \
        "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/purge_cache" \
        -H "Authorization: Bearer $CF_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"files\":[$purge_urls]}"
fi

# 4. 验证 Purge 效果
sleep 5
for file in $changed_files; do
    url="https://www.example.com/${file#public/}"
    cache_status=$(curl -sI "$url" | grep -i "cf-cache-status" | awk '{print $2}')
    echo "$url$cache_status"
done

echo "Deploy and purge complete."

GitHub Actions 集成

# .github/workflows/deploy.yml
name: Deploy and Purge CDN
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2  # 需要对比前一个 commit
      
      - name: Build
        run: npm run build
      
      - name: Deploy to Origin
        run: |
          rsync -avz ./dist/ ${{ secrets.ORIGIN_SERVER }}:/var/www/html/
      
      - name: Purge CDN Cache
        run: |
          # 获取变更文件
          CHANGED=$(git diff --name-only HEAD~1 HEAD -- dist/ | \
            sed 's|^dist/|https://www.example.com/|')
          
          if [ -n "$CHANGED" ]; then
            # 构建 JSON 数组
            FILES=$(echo "$CHANGED" | jq -R . | jq -s .)
            curl -s -X POST \
              "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE_ID }}/purge_cache" \
              -H "Authorization: Bearer ${{ secrets.CF_TOKEN }}" \
              -H "Content-Type: application/json" \
              -d "{\"files\":$FILES}"
          fi

3.6 Purge 的一致性问题

Purge 不是瞬时的——从发起 Purge 到全球所有 PoP 生效需要时间:

Purge 传播时间(参考):
- Fastly Instant Purge: ~150ms
- Cloudflare: 2-5 秒
- CloudFront: 5-15 分钟(Invalidation)
- 传统 CDN: 5-30 分钟

一致性窗口问题:
T=0s:   内容更新 + 发起 Purge
T=0-5s: 部分 PoP 收到 Purge,部分还在服务旧内容
T=5s+:  所有 PoP 生效

在一致性窗口内:
- 不同地区的用户可能看到不同版本
- 同一用户刷新页面可能看到新旧交替(DNS 解析到不同 PoP)

四、stale-while-revalidate 深入

stale-while-revalidate(SWR)是 CDN 缓存策略中最被低估的功能之一——它在缓存过期时返回旧内容,同时后台异步刷新。

4.1 SWR 的工作原理

Cache-Control: public, max-age=3600, stale-while-revalidate=86400

时间线:
T=0:     资源被缓存
T=0-3600: 缓存新鲜 → 直接返回(200)
T=3600:   缓存过期

T=3600-90000: stale-while-revalidate 窗口
  ├── 请求到达 → 立即返回旧内容(零延迟)
  ├── 同时后台向源站发请求刷新
  └── 刷新成功 → 更新缓存(后续请求得到新内容)

T>90000: 超出 SWR 窗口
  └── 必须同步回源(和无 SWR 一样)

4.2 SWR vs 普通缓存

场景:TTL 过期后第一个请求的用户体验

无 SWR(Cache-Control: max-age=3600):
  用户请求 → 缓存过期 → 同步回源(200ms)→ 返回新内容
  用户延迟:200ms

有 SWR(Cache-Control: max-age=3600, stale-while-revalidate=86400):
  用户请求 → 缓存过期但在 SWR 窗口 → 立即返回旧内容
  同时后台回源 → 更新缓存
  用户延迟:<1ms(本地缓存速度)

4.3 SWR 的适用场景

适合 SWR 的内容:
├── 新闻列表:几分钟的延迟可接受
├── 商品列表:价格变化通常不需要秒级一致
├── 用户头像:偶尔看到旧头像没关系
├── 配置数据:缓存中的旧配置可用
└── 搜索结果:搜索索引本身就有延迟

不适合 SWR 的内容:
├── 库存数量:过期数据导致超卖
├── 实时价格:金融数据需要精确
├── 安全策略:安全配置不允许过期
├── 已撤回内容:法律/合规要求立即下线
└── 用户会话数据:不应缓存

4.4 stale-if-error

stale-if-error 是 SWR 的姊妹指令——当源站出错(5xx 或网络错误)时返回过期缓存:

Cache-Control: public, max-age=3600, stale-if-error=86400

正常情况:TTL 过期 → 同步回源 → 返回新内容
源站故障时:TTL 过期 → 回源失败(503)→ 返回旧缓存

stale-if-error 的防御价值:
- 源站宕机 → 用户仍能看到内容(虽然可能过期)
- 网络分区 → CDN 降级为静态站点
- 是"优雅降级"的关键机制

Nginx 配置 SWR + stale-if-error

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=main:10m 
    max_size=10g inactive=60m;

server {
    location / {
        proxy_cache main;
        proxy_cache_valid 200 1h;
        
        # 在后台刷新时返回旧内容
        proxy_cache_use_stale updating;
        
        # 源站出错时返回旧内容
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
        
        # 后台异步刷新
        proxy_cache_background_update on;
        
        # 请求合并(防止回源风暴)
        proxy_cache_lock on;
        proxy_cache_lock_timeout 5s;
    }
}

五、缓存命中率优化

缓存命中率是 CDN 效果的核心指标。从 60% 到 90% 的提升可以显著减少源站负载和用户延迟。

5.1 命中率低的常见原因

缓存命中率低的诊断清单:

1. TTL 太短
   → max-age=60 的资源每分钟都要回源
   → 检查:curl -sI URL | grep cache-control

2. Cache Key 包含无关参数
   → utm_source、fbclid 等参数导致每个 URL 都不同
   → 检查:对比带参数和不带参数的 X-Cache

3. Vary 头设置过宽
   → Vary: * → 完全不可缓存
   → Vary: Cookie → 每个用户独立缓存
   → 检查:curl -sI URL | grep vary

4. Set-Cookie 阻止缓存
   → 响应中包含 Set-Cookie → 大多数 CDN 不缓存
   → 检查:curl -sI URL | grep set-cookie

5. 私有缓存控制
   → Cache-Control: private → CDN 不缓存
   → 检查:确认 API 不必要地设了 private

6. 缓存容量不足
   → 热门内容被冷门内容驱逐(LRU)
   → 检查:CDN 控制台的 eviction 统计

7. URL 规范化问题
   → /path 和 /path/ 被视为不同 URL
   → /Path 和 /path 被视为不同 URL(大小写)
   → 尾部斜杠和大小写不一致

5.2 优化措施

# 1. Query String 排序与过滤
# 使用 Lua 或 CDN 配置过滤营销参数

# OpenResty Lua 示例
location / {
    set_by_lua_block $clean_uri {
        local uri = ngx.var.uri
        local args = ngx.req.get_uri_args()
        
        -- 删除追踪参数
        args["utm_source"] = nil
        args["utm_medium"] = nil
        args["utm_campaign"] = nil
        args["fbclid"] = nil
        args["gclid"] = nil
        
        -- 排序参数
        local sorted_args = {}
        for k, v in pairs(args) do
            table.insert(sorted_args, k .. "=" .. v)
        end
        table.sort(sorted_args)
        
        return uri .. "?" .. table.concat(sorted_args, "&")
    }
    
    proxy_cache_key "$scheme$host$clean_uri";
}

# 2. URL 规范化
# 统一尾部斜杠
location / {
    rewrite ^/(.*)/$ /$1 permanent;
}

# 3. 分离可缓存和不可缓存的响应
# 不要在可缓存的 API 响应中设置 Set-Cookie
location /api/public/ {
    proxy_pass http://backend;
    proxy_hide_header Set-Cookie;  # 隐藏后端设置的 Cookie
    proxy_ignore_headers Set-Cookie;
    add_header Cache-Control "public, s-maxage=300";
}

5.3 缓存命中率监控

# 1. 从 CDN 日志统计命中率
# Cloudflare 日志
awk -F',' '{
    total++; 
    if ($cache_status == "HIT") hit++
} END {
    printf "Hit Rate: %.1f%% (%d/%d)\n", hit/total*100, hit, total
}' cdn-log.csv

# 2. 实时采样检测
#!/bin/bash
# sample-cache-status.sh
URLS=(
    "https://www.example.com/"
    "https://www.example.com/static/app.js"
    "https://www.example.com/api/products"
)

for url in "${URLS[@]}"; do
    status=$(curl -sI "$url" | grep -i "x-cache\|cf-cache-status" | head -1)
    echo "$url$status"
done

# 3. Prometheus 指标
# CloudFront 通过 CloudWatch 暴露
# CacheHitRate = Hits / (Hits + Misses)
# 目标:静态资源 > 95%,动态内容 > 70%

5.4 预热策略

新部署或 Purge 后,缓存是空的。预热可以避免回源风暴:

#!/bin/bash
# cdn-warm.sh — CDN 缓存预热脚本

SITEMAP_URL="https://www.example.com/sitemap.xml"

# 从 sitemap 提取 URL
urls=$(curl -s "$SITEMAP_URL" | grep -oP '(?<=<loc>).*?(?=</loc>)')

# 并发预热(限制并发数避免压垮源站)
echo "$urls" | xargs -P 10 -I{} curl -s -o /dev/null -w "%{http_code} %{url_effective}\n" "{}"

# 对关键页面从多个地区预热
# 使用 CDN 的 PoP 列表
POPS=("iad" "lax" "lhr" "nrt" "sin")
for pop in "${POPS[@]}"; do
    echo "Warming from $pop..."
    curl -s -o /dev/null \
        -H "X-CDN-Pop: $pop" \
        "https://www.example.com/"
done

六、常见缓存问题排查

6.1 排查清单

现象 可能原因 排查命令
X-Cache 始终 MISS TTL=0 / no-store / Vary:* curl -sI URL \| grep -i cache
返回旧内容 Purge 未生效 / 浏览器缓存 强制刷新 + 检查 Age 头
不同地区内容不同 Purge 传播延迟 从不同 PoP 测试
命中率低 Query String / Cookie / Vary 对比有参数和无参数
缓存了不该缓存的 源站 Cache-Control 错误 检查源站响应头
文件下载不完整 缓存了 206 Partial Content 检查 Range 请求处理

6.2 调试示例

# 案例:API 响应缓存命中率低

# Step 1: 检查源站响应头
curl -sI https://origin.example.com/api/products
# Cache-Control: no-cache
# Set-Cookie: session=abc123
# Vary: Cookie, Accept-Encoding

# 问题发现:
# 1. no-cache → 每次都要验证
# 2. Set-Cookie → CDN 不缓存
# 3. Vary: Cookie → 每个用户独立缓存

# 修复:
# 1. 公开 API 使用 s-maxage 而非 no-cache
# 2. 公开 API 不设 Set-Cookie
# 3. Vary 只保留 Accept-Encoding

# Step 2: 验证修复
curl -sI https://www.example.com/api/products
# Cache-Control: public, s-maxage=300
# Vary: Accept-Encoding
# X-Cache: HIT
# Age: 45   ← 缓存成功

# Step 3: 监控命中率变化
# 修复前:12% hit rate
# 修复后:85% hit rate

七、总结

CDN 缓存策略是性能工程的核心环节——正确的策略能把源站请求减少 90%,错误的策略会让 CDN 形同虚设。

我的工程建议

  1. 采用版本化资源 + 短 TTL 入口文件的策略。带哈希的 JS/CSS/图片用 max-age=31536000, immutable;HTML 入口用 no-cache 或短 max-age。这是最安全、最高效的缓存模式。

  2. s-maxage 应该大于 max-age。CDN 缓存比浏览器缓存服务的用户多得多,命中率更重要。让 CDN 缓存更久,浏览器缓存更短——既减少回源,又保证更新速度。

  3. stale-while-revalidate 应该成为默认配置。对于不需要秒级一致性的内容(大多数内容),SWR 让 TTL 过期时的用户体验从”等待回源”变成”零延迟返回”。

  4. 缓存命中率优化的 80/20 法则。过滤 UTM 参数、修复 Vary 头、去掉不必要的 Set-Cookie——这三项通常能解决 80% 的命中率问题。

  5. Purge 不是缓存策略的核心。如果你频繁依赖 Purge 来保证内容新鲜度,说明 TTL 策略设计有问题。合理的 TTL + 版本化资源应该让 Purge 成为”偶尔使用的紧急工具”而不是”每次发布都要做的操作”。

  6. stale-if-error 是免费的可用性保险。源站故障时返回旧缓存比返回 502 好得多。几乎没有理由不开启它。


上一篇:CDN 架构原理:PoP、边缘节点与 Origin Shield

下一篇:动态加速:TCP 优化、路由优化与边缘计算

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-04-22 · network

网络工程索引

汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。


By .