CDN 的核心是缓存——缓存策略的好坏直接决定了命中率、源站负载和用户体验。但缓存策略的设计远比”设个 TTL”复杂:
- 静态资源和动态 API 的 TTL 应该分别设多少?
- 内容更新后,CDN 缓存的旧内容怎么及时清除?
- stale-while-revalidate 和 stale-if-error 到底有什么区别?
- 缓存命中率从 60% 提升到 90% 的关键优化点在哪里?
这些问题没有”万能答案”——它们取决于你的内容特征、更新频率和一致性要求。本文从 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=60 或
no-cache |
需要较高新鲜度 |
| 用户个性化数据 | 0 | private, no-store |
不应缓存 |
| 搜索结果 | 5-60 秒 | public, s-maxage=30 |
短 TTL 平衡新鲜度 |
| 实时数据(股价等) | 0 | no-cache 或 no-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 response3.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}"
fi3.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 形同虚设。
我的工程建议:
采用版本化资源 + 短 TTL 入口文件的策略。带哈希的 JS/CSS/图片用
max-age=31536000, immutable;HTML 入口用no-cache或短max-age。这是最安全、最高效的缓存模式。s-maxage 应该大于 max-age。CDN 缓存比浏览器缓存服务的用户多得多,命中率更重要。让 CDN 缓存更久,浏览器缓存更短——既减少回源,又保证更新速度。
stale-while-revalidate 应该成为默认配置。对于不需要秒级一致性的内容(大多数内容),SWR 让 TTL 过期时的用户体验从”等待回源”变成”零延迟返回”。
缓存命中率优化的 80/20 法则。过滤 UTM 参数、修复 Vary 头、去掉不必要的 Set-Cookie——这三项通常能解决 80% 的命中率问题。
Purge 不是缓存策略的核心。如果你频繁依赖 Purge 来保证内容新鲜度,说明 TTL 策略设计有问题。合理的 TTL + 版本化资源应该让 Purge 成为”偶尔使用的紧急工具”而不是”每次发布都要做的操作”。
stale-if-error 是免费的可用性保险。源站故障时返回旧缓存比返回 502 好得多。几乎没有理由不开启它。
上一篇:CDN 架构原理:PoP、边缘节点与 Origin Shield
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】CDN 架构原理:PoP、边缘节点与 Origin Shield
系统解剖 CDN 的多层缓存架构——从 DNS 调度到 PoP 内部结构、Origin Shield 回源保护、多 CDN 部署策略。结合实际配置和响应头分析,给出 CDN 架构的工程理解。
【网络工程】HTTP 缓存工程:Cache-Control 全景与条件请求
系统剖析 HTTP 缓存体系:Cache-Control 指令全表、ETag 与 Last-Modified 的条件请求、Vary 头的工程陷阱、CDN 与浏览器缓存的交互行为。从缓存策略设计到缓存失效排查的完整工程实践。
网络工程索引
汇总本站网络工程系列文章,覆盖分层模型、以太网、IP、TCP、DNS、TLS、HTTP/2/3、CDN、BGP 与故障诊断。
【网络工程】动态加速:TCP 优化、路由优化与边缘计算
CDN 对静态资源的加速已成共识,但动态 API 请求同样可以受益于 CDN 的网络基础设施。本文从 TCP 优化、智能路由到边缘计算三个层次,拆解动态加速的技术原理、架构选型与工程实践。