图像缓存策略完全指南 - Cache-Control、ETag 与 CDN 配置
Web 图像缓存基础 - 浏览器缓存与 CDN 缓存的角色
图像通常占网页总传输量的 50-70%。有效的缓存策略可以消除重复下载,显著提升页面加载速度和用户体验。
缓存层次:浏览器内存缓存(最快,页面会话内有效)→ 浏览器磁盘缓存(较快,跨会话持久化)→ CDN 边缘缓存(减少源站负载)→ 源站。每一层都可以独立配置缓存策略。
缓存的核心权衡:缓存时间越长,性能越好(减少请求),但内容更新的延迟也越大。图像的特点是内容一旦发布很少修改,因此适合长时间缓存。关键是设计一个在需要更新时能可靠失效的机制。
不可变资源策略:对于文件名包含内容哈希的图像(如 hero-a3f2b1.jpg),可以设置极长的缓存时间(1 年),因为内容变化时文件名也会变化,浏览器会请求新 URL。这是最高效的缓存模式。
Cache-Control 头设计 - 指令组合
Cache-Control 是控制 HTTP 缓存行为的核心响应头。
常用指令:max-age=31536000(缓存 1 年)、no-cache(每次使用前必须验证)、no-store(完全不缓存)、public(CDN 可缓存)、private(仅浏览器可缓存)、immutable(内容永不变化,无需验证)。
推荐组合:
- 带哈希的静态图像:
Cache-Control: public, max-age=31536000, immutable - 可能更新的图像:
Cache-Control: public, max-age=86400, must-revalidate - 用户头像等动态图像:
Cache-Control: public, max-age=3600, must-revalidate - 敏感图像:
Cache-Control: private, no-store
immutable 指令告诉浏览器即使用户强制刷新也不需要重新验证,消除了不必要的条件请求。Chrome 和 Firefox 支持此指令。
stale-while-revalidate=86400 允许在后台重新验证期间继续使用过期缓存,用户不会感知到延迟。
ETag 与条件请求 - 高效的缓存重验证
当缓存过期时,条件请求允许服务器确认内容是否真的变化了,避免重新传输未修改的图像。
ETag 工作流程:服务器在响应中包含 ETag: "abc123"(通常是内容的哈希值)。浏览器缓存过期后,发送 If-None-Match: "abc123" 条件请求。如果内容未变,服务器返回 304 Not Modified(无响应体),浏览器继续使用缓存。
Last-Modified / If-Modified-Since:基于时间戳的替代方案。精度为秒级,不如 ETag 精确,但实现更简单。两者可以同时使用,ETag 优先级更高。
强 ETag vs 弱 ETag:强 ETag("abc123")表示字节级相同;弱 ETag(W/"abc123")表示语义等价(允许不重要的差异如空白变化)。图像通常使用强 ETag。
性能影响:304 响应虽然不传输图像数据,但仍有一次网络往返。对于带哈希文件名的图像,使用长 max-age + immutable 完全避免条件请求是更优方案。ETag 适合文件名不变但内容可能更新的场景。
CDN 缓存设计 - CloudFront 与 Cloudflare 配置
CDN 在全球边缘节点缓存图像,减少源站负载并降低用户延迟。
CloudFront 配置:通过缓存策略(Cache Policy)控制 CDN 缓存行为。推荐为图像创建专用缓存策略:TTL 设为 86400-31536000 秒,启用 Gzip/Brotli 压缩(对 SVG 有效),根据 Accept 头进行内容协商(WebP/AVIF 自动选择)。
Cloudflare 配置:Page Rules 或 Cache Rules 设置图像路径的缓存级别。Browser Cache TTL 控制浏览器缓存时间。Polish 功能自动优化图像格式和质量。
缓存键设计:CDN 根据缓存键区分不同版本。默认缓存键是完整 URL。如果使用内容协商(同一 URL 返回不同格式),需要将 Accept 头加入缓存键,否则所有用户会收到第一个被缓存的格式。
源站保护:配置 CDN 的 Origin Shield(CloudFront)或 Tiered Caching(Cloudflare),减少回源请求。多个边缘节点的缓存未命中会合并为单次回源请求。
缓存失效策略 - 确保图像更新被及时反映
缓存失效是缓存系统中最困难的问题之一。需要在缓存效率和内容新鲜度之间找到平衡。
文件名哈希(推荐):在文件名中包含内容哈希(如 logo-v2f3a1b.png)。内容变化时哈希变化,URL 变化,浏览器自动请求新文件。无需主动失效缓存。构建工具(Webpack、Vite)自动处理。
CDN 缓存清除:通过 API 主动清除 CDN 上的缓存。CloudFront 使用 Invalidation(每月前 1000 次免费);Cloudflare 使用 Purge Cache。适合紧急更新但不应作为常规手段。
版本查询参数:image.jpg?v=2。简单但有缺陷 - 部分 CDN 默认忽略查询参数,需要配置将其纳入缓存键。不如文件名哈希可靠。
短 TTL + 条件请求:设置较短的 max-age(如 1 小时),过期后通过 ETag 验证。平衡了新鲜度和性能,但增加了条件请求的网络开销。
最佳实践:静态资源使用文件名哈希 + 长缓存;动态图像使用短 TTL + ETag;紧急更新使用 CDN Invalidation 作为补充手段。
Service Worker 高级缓存控制 - 离线支持
Service Worker 提供了完全可编程的缓存层,实现离线访问和精细的缓存策略。
缓存优先策略(Cache First):先检查缓存,命中则直接返回;未命中则请求网络并缓存结果。适合不常变化的图像资源。用户体验最快但可能显示过期内容。
网络优先策略(Network First):先请求网络,成功则更新缓存并返回;失败则回退到缓存。适合需要新鲜度但也需要离线支持的场景。
Stale While Revalidate:立即返回缓存内容(即使过期),同时在后台请求网络更新缓存。用户看到即时响应,下次访问看到更新内容。最佳的用户体验和新鲜度平衡。
图像专用缓存:创建独立的 Cache Storage 存储图像,设置大小上限(如 50MB)。超出限制时使用 LRU(最近最少使用)策略淘汰旧图像。
预缓存关键图像:在 Service Worker 安装阶段预缓存 Logo、图标等关键图像,确保离线时核心 UI 完整显示。使用 Workbox 库简化 Service Worker 的缓存管理。