图像缓存策略完全指南 - Cache-Control、ETag 与 CDN 配置

· 9 分钟阅读

Web 图像缓存基础 - 浏览器缓存与 CDN 缓存的角色

图像通常占网页总传输量的 50-70%。有效的缓存策略可以消除重复下载,显著提升页面加载速度和用户体验。

缓存层次:浏览器内存缓存(最快,页面会话内有效)→ 浏览器磁盘缓存(较快,跨会话持久化)→ CDN 边缘缓存(减少源站负载)→ 源站。每一层都可以独立配置缓存策略。

缓存的核心权衡:缓存时间越长,性能越好(减少请求),但内容更新的延迟也越大。图像的特点是内容一旦发布很少修改,因此适合长时间缓存。关键是设计一个在需要更新时能可靠失效的机制。

不可变资源策略:对于文件名包含内容哈希的图像(如 hero-a3f2b1.jpg),可以设置极长的缓存时间(1 年),因为内容变化时文件名也会变化,浏览器会请求新 URL。这是最高效的缓存模式。

再补充几点:具有代表性的 CDN 有 CloudFront、Cloudflare、Fastly 等。根据 HTTP Archive 的调查,只要缓存设置得当,图像请求的 60-80% 可以直接由缓存提供。设计缓存策略时,关键在于平衡「缓存的新鲜度(freshness)」与「缓存的失效(invalidation)」。

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 允许在后台重新验证期间继续使用过期缓存,用户不会感知到延迟。

不变的静态图像(带哈希的文件名)用 Cache-Control: public, max-age=31536000, immutable。文件名中包含内容哈希的图像(例如 hero-a1b2c3d4.jpg)在内容变化时文件名也会变化,因此可以安全地设置 1 年(31536000 秒)的缓存。immutable 指令表示浏览器连缓存的重新验证(conditional request)都不会执行。

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 适合文件名不变但内容可能更新的场景。

ETag(Entity Tag)是标识资源某个特定版本的字符串。工作流程是:首次请求时服务器在响应中附加 ETag: "abc123";缓存过期后再次请求时浏览器发送 If-None-Match: "abc123" 头;内容没有变化则服务器返回 304 Not Modified(不含正文,仅数百字节),有变化则返回 200 OK 与新的图像数据。生成方式上,强 ETag 由文件的内容哈希(MD5、SHA-256 等)生成;而基于 inode 的 ETag(Apache 的默认设置)在每台服务器上都不同,因此应改为基于内容哈希的方式。

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),减少回源请求。多个边缘节点的缓存未命中会合并为单次回源请求。

两大 CDN 的具体设置:CloudFront 的缓存策略可以使用托管策略 CachingOptimized,它依据 Cache-ControlExpires 头缓存,也能控制是否把查询字符串纳入缓存键;源请求策略则在需要把 Accept 头转发给源站、启用 WebP/AVIF 的 Content Negotiation 时设置。Cloudflare 的 Browser Cache TTL 在仪表盘中设置,选择「Respect Existing Headers」即直接沿用源站的 Cache-Control;Edge Cache TTL 用 Page Rules 按 URL 模式设置,常见做法是对 /images/* 设置为 1 个月;Polish 是图像自动优化功能,可以在 CDN 边缘执行 WebP 转换与无损压缩。缓存键除 URL 路径外还需包含用于格式协商的 Accept 头,这样同一个 URL 也能对支持 WebP 的浏览器返回 WebP、对不支持的浏览器返回 JPEG;实现方式是在 Vary 头中包含 Accept

缓存失效策略 - 确保图像更新被及时反映

缓存失效是缓存系统中最困难的问题之一。需要在缓存效率和内容新鲜度之间找到平衡。

文件名哈希(推荐):在文件名中包含内容哈希(如 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 作为补充手段。

失效策略有两种典型写法。策略 1 是文件名哈希:把图像命名为 hero-image-a1b2c3d4e5f6.jpg,图像更新时哈希改变、URL 也随之改变,旧缓存的问题被彻底解决;由于 HTML 侧的引用也要同时更新,前提是与构建流水线集成,用 Webpack 的 [contenthash] 或 Vite 的资源哈希功能即可自动化。策略 2 是用查询字符串做版本管理:写作 hero-image.jpg?v=20250723,不改文件名而用查询字符串管理版本。紧急更新时的手动清除,在 CloudFront 用 CreateInvalidation API、在 Cloudflare 用 Purge Cache API 执行。

Service Worker 高级缓存控制 - 离线支持

Service Worker 提供了完全可编程的缓存层,实现离线访问和精细的缓存策略。

缓存优先策略(Cache First):先检查缓存,命中则直接返回;未命中则请求网络并缓存结果。适合不常变化的图像资源。用户体验最快但可能显示过期内容。

网络优先策略(Network First):先请求网络,成功则更新缓存并返回;失败则回退到缓存。适合需要新鲜度但也需要离线支持的场景。

Stale While Revalidate:立即返回缓存内容(即使过期),同时在后台请求网络更新缓存。用户看到即时响应,下次访问看到更新内容。最佳的用户体验和新鲜度平衡。

图像专用缓存:创建独立的 Cache Storage 存储图像,设置大小上限(如 50MB)。超出限制时使用 LRU(最近最少使用)策略淘汰旧图像。

预缓存关键图像:在 Service Worker 安装阶段预缓存 Logo、图标等关键图像,确保离线时核心 UI 完整显示。使用 Workbox 库简化 Service Worker 的缓存管理。

Cache First 策略(最适合图像)的具体写法是 self.addEventListener("fetch", (event) => { if (event.request.destination === "image") { event.respondWith(caches.match(event.request).then((cached) => { return cached || fetch(event.request).then(async (response) => { const cache = await caches.open("images-v1"); cache.put(event.request, response.clone()); return response; }); })); } });,缓存命中时立即返回,只有未命中时才发起网络请求。容量管理方面,Service Worker 的缓存存储有上限(因浏览器而异,通常为 50MB-数百 MB),应实现用 LRU(Least Recently Used)自动删除旧条目的逻辑;使用 Workbox 库的 ExpirationPlugin 可以轻松设置最大条目数与最长保存期限。响应式图像方面,用 srcset 提供多种尺寸时,在 Service Worker 中只缓存与设备屏幕宽度相符的最佳尺寸,即可减少存储占用。

相关文章

图像 CDN 搭建与优化 - 使用 CloudFront 和 Cloudflare 实现高速分发

学习图像 CDN 的搭建方法和优化策略,包括 CloudFront Lambda@Edge 图像处理、Cloudflare 图像优化和缓存命中率最大化。

大规模站点的图像分发架构 - 设计模式与实现

大规模图像分发的架构模式。涵盖 CDN 设计、源站配置、动态变换和高流量站点的缓存策略。

图像转换 API 设计模式 - URL 方式、请求体方式与异步处理的对比

探索图像转换 API 的架构设计模式。对比 URL 参数方式、REST API 方式和异步队列方式,为生产系统提供可扩展的设计指南。

通过内容协商提供最优图像 - Accept 头与 CDN 集成

利用 HTTP 内容协商自动为浏览器提供最优图像格式。涵盖服务器配置、CDN 集成、Vary 头管理及生产环境运维。

Web 图像文件大小优化策略 - 在保持质量的同时减小体积

系统讲解 Web 图像文件大小优化技术。涵盖格式选择、分辨率优化、元数据清理、CDN 分发优化和自动化质量管理。

图像指纹技术 - 使用 pHash 和 dHash 检测相似图像

系统讲解感知哈希技术检测相似图像。涵盖 aHash、dHash、pHash 的原理,汉明距离评分和实际系统架构。

相关术语