欢迎光临
我们一直在努力

前端 CDN 缓存策略调优:静态资源、HTML 与 API 分层设计(续篇)

前端 CDN 缓存策略调优:静态资源、HTML 与 API 分层设计(续篇)

CDN 缓存策略一刀切?静态资源缓存一年没问题,HTML 缓存一年就炸了。

一、场景痛点

你的前端应用部署后,CDN 配了统一的 Cache-Control: max-age=86400(1 天缓存)。静态资源(JS/CSS)1 天缓存没问题——文件名包含 contenthash,内容变了文件名也变。但 HTML 也被缓存了 1 天,新版本上线后用户看到的是旧 HTML,旧 HTML 引用旧 JS,新版本永远不会被加载。

你把 HTML 的缓存改成 no-cache,但 CDN 的 no-cache 行为不一致——有些 CDN 把 no-cache 当成"不缓存",有些当成"缓存但每次验证"。你需要在 CDN 配置层面把静态资源和 HTML 分开:静态资源长缓存,HTML 短缓存或不缓存。

核心矛盾:CDN 缓存策略必须分层:静态资源缓存 1 年,HTML 缓存 5 分钟或不缓存,API 不缓存。一层策略不能覆盖三种资源类型。

二、底层机制与原理剖析

2.1 三种资源类型的缓存需求

2.2 CDN 的 Cache-Control 行为差异

不同 CDN 对 Cache-Control 的解释不同:

Cache-ControlCloudflareAkamai阿里云 CDN
max-age=31536000 缓存1年 ✅ 缓存1年 ✅ 缓存1年 ✅
max-age=0, no-cache 每次回源验证 每次回源验证 可能缓存(需配回源)
no-store 不缓存 不缓存 不缓存 ✅
stale-while-revalidate=300 支持SWR 支持SWR 不支持SWR

关键:no-cache 在部分 CDN 上不是"不缓存"而是"缓存但每次验证"。如果你的 CDN 不支持 SWR,HTML 的最佳策略是 max-age=300(5 分钟缓存)而不是 no-cache。

三、生产级代码实现

3.1 Nginx 分层缓存配置

# nginx-cdn.conf —— 分层缓存策略配置

# 静态资源:长期缓存 + immutable
location /assets/ {
root /usr/share/nginx/html;
# 一年缓存 + immutable:文件名含 contenthash,内容永远不变
add_header Cache-Control "public, max-age=31536000, immutable";
etag off; # 不需要 ETag:文件名已经是唯一标识
gzip on;
}

# HTML 入口:短缓存 + stale-while-revalidate
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
# 5 分钟缓存 + SWR:用户拿到缓存版本(快),后台验证更新
# 如果 CDN 不支持 SWR,退化为 max-age=300
add_header Cache-Control "public, max-age=300, stale-while-revalidate=300";
etag on; # ETag 辅助验证:HTML 内容没变时返回 304
}

# API 请求:不缓存(通过 Nginx 代理转发到后端)
location /api/ {
proxy_pass http://backend-service:8080;
# API 数据动态,不缓存
add_header Cache-Control "no-store, no-cache, must-revalidate";
# 代理缓存也关闭:Nginx 不缓存后端响应
proxy_cache off;
proxy_no_cache 1;
}

# Service Worker:不缓存(每次必须拿到最新版)
location /sw.js {
root /usr/share/nginx/html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}

3.2 CDN 主动 purge 策略

# cdn_purge_strategy.py —— 部署后主动 purge HTML 缓存
import logging
import requests
import time

logger = logging.getLogger('cdn-purge')

class CDNPurgeManager:
"""部署后主动 purge HTML 缓存,不 purge 静态资源"""

def purge_after_deploy(self, domain: str, api_key: str):
"""部署完成后 purge HTML 的 CDN 缓存"""
# 只 purge HTML:静态资源靠 contenthash 自然更新
# 如果 purge 全站,静态资源的缓存也失效了(损失巨大)
urls_to_purge = [
f'https://{domain}/',
f'https://{domain}/index.html',
]

for url in urls_to_purge:
try:
response = requests.post(
'https://api.cdn-provider.com/purge',
headers={'Authorization': f'Bearer {api_key}'},
json={'urls': [url]},
timeout=10,
)
if response.status_code == 200:
logger.info(f'Purged CDN cache for: {url}')
else:
logger.warning(f'Purge failed: {response.status_code}')
except requests.Timeout:
# CDN purge 超时不阻断部署:缓存会在 max-age 到期后自然更新
logger.warning(f'CDN purge timeout for {url}')

# 验证:部署后 5 秒检查新版本是否可访问
time.sleep(5)
self._verify_new_version(domain)

def _verify_new_version(self, domain: str):
"""验证新版本是否在 CDN 上可见"""
try:
response = requests.get(
f'https://{domain}/',
headers={'Cache-Control': 'no-cache'}, # 绕过本地缓存
timeout=10,
)
# 检查 HTML 中是否包含新版本的特征
# 比如新版本号或新的 JS 文件名
logger.info(f'Verification response status: {response.status_code}')
except Exception as e:
logger.warning(f'Verification failed: {e}')

四、边界分析与架构权衡

4.1 SWR 的用户体验代价

SWR 模式下,用户第一次访问拿到旧版本(缓存),后台验证发现新版本后,第二次访问才拿到新版本。这意味着大约 5 分钟内,部分用户看到旧版本。

对策:如果业务不能容忍 5 分钟延迟(比如紧急修复),部署后主动 purge HTML 的 CDN 缓存。

4.2 API 缓存的特殊场景

某些 API 数据可以短期缓存:产品列表(每小时更新)、配置信息(不频繁变更)。这些 API 可以设 max-age=300 + stale-while-revalidate=3600,减少后端压力。

4.3 适用边界与禁用场景

  • 适用:CDN 加速的前端应用、静态资源与 HTML 分离部署、需要不同缓存策略的多资源类型
  • 禁用:纯 SSR 应用(没有静态资源)、内网应用(没有 CDN)、所有资源类型相同的应用

五、结语

CDN 缓存策略必须分层:静态资源缓存 1 年+immutable(文件名含 contenthash 保证更新),HTML 缓存 5 分钟+SWR(短期缓存+后台验证更新),API 不缓存(动态数据每次回源)。分层的关键是 Nginx 按路径分别配置 Cache-Control。部署后主动 purge HTML 的 CDN 缓存(不 purge 静态资源),保证新版本在 5 分钟内对所有用户可见。no-cache 在部分 CDN 上行为不一致,HTML 用 max-age=300 更可靠。API 的特殊场景(产品列表等)可以短期缓存,减少后端压力。

资料说明

本文中的协议、版本、性能、成本和行业趋势应以可核验的一手资料为准。未标注统计口径的比例、时间表和预测仅作工程讨论,不应视为行业事实。可参考 0730 资料来源索引,并在发布前将具体来源贴到对应断言之后。

赞(0)
未经允许不得转载:171主机测评 » 前端 CDN 缓存策略调优:静态资源、HTML 与 API 分层设计(续篇)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址