Web 应用的浏览器缓存实用指南

发布: (2026年1月7日 GMT+8 10:59)
12 min read
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将为您翻译成简体中文并保持原有的格式、Markdown 语法以及技术术语不变。谢谢!

什么是浏览器缓存

  • 浏览器缓存 – 本地于用户设备(重复访问的最快路径)。
  • CDN / 代理缓存 – 位于靠近用户的边缘服务器的副本(降低源站负载和延迟)。
  • Service‑worker 缓存(可选) – 应用控制的缓存逻辑,用于离线和高级更新策略。

Why caching matters

  • Performance – 回访者会看到显著的速度提升。
  • Cost – 更少的字节离开您的源站和 API。
  • Reliability – 在流量高峰和故障期间,服务器负载更低。

主要挑战:部署后保持新鲜

金色模式(简单可靠)

  1. HTML 在每次导航时重新验证。
  2. 静态资源(JS、CSS、图片)拥有较长的缓存生命周期,但当内容更改时文件名会改变。
  3. API 使用 ETagLast‑Modified 进行频繁重新验证。

如何实现它

1️⃣ 静态资源的内容哈希文件名

2️⃣ Cache‑Control 响应头

资源响应头示例原因
HTMLCache-Control: no-cache, must-revalidate (or max-age=0, must-revalidate)告诉浏览器和 CDN 在使用缓存副本前先向服务器确认。配合 ETagLast‑Modified,检查成本低且快速。
哈希资源(JS/CSS/图片/字体)Cache-Control: public, max-age=31536000, immutable因为 URL 与内容唯一对应,可长期缓存。
APICache-Control: no-cache (or a short max-age with must-revalidate) + ETag or Last‑Modified当数据未变化时,客户端可快速收到 304 Not Modified 响应。

3️⃣ 部署顺序

  1. 首先上传新的哈希资源。
  2. 发布引用这些新资源文件名的更新后 HTML。
  3. (可选) 为 HTML 路径刷新 CDN 缓存,以便快速传播更新的入口页面。

何时使用缓存

  • Production – 始终。它是基础的性能实践。
  • Development – 保持缓存最小化以避免混淆(例如,在 DevTools 中禁用缓存或使用较短的 max-age)。
  • Private or sensitive content – 对机密页面或数据使用更严格的标头,例如 Cache-Control: no-store

什么是“no‑cache”真正的含义

意味着 “don’t store”;它意味着 “must revalidate with the origin before using a cached copy.”

部署后的示例用户流程

  1. 你部署了一个构建

    • main.js 发生变化 → 变为 main.<hash>.js
    • styles.css 未改变 → 仍为 styles.<hash>.css
    • index.html 已更新,以引用新的文件名
  2. 用户访问时

    • 浏览器重新验证 index.html 并获取更新后的 HTML。
    • 下载 main.<hash>.js(新 URL)。
    • 复用已缓存的 styles.<hash>.css(相同 URL)。

结果: 仅获取已更改的文件;未更改的文件瞬间加载。

配置示例(概念性)

Nginx

# HTML – always revalidate
location = /index.html {
    add_header Cache-Control "no-cache, must-revalidate";
}

# Static assets – long‑lived immutable cache
location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

Apache (.htaccess)

# HTML
Header set Cache-Control "no-cache, must-revalidate"

# Static assets
Header set Cache-Control "public, max-age=31536000, immutable"

Node / Express

app.use(
  express.static("dist", {
    setHeaders: (res, path) => {
      if (path.endsWith(".html")) {
        res.setHeader("Cache-Control", "no-cache, must-revalidate");
      } else {
        res.setHeader(
          "Cache-Control",
          "public, max-age=31536000, immutable"
        );
      }
    },
  })
);

Framework tips

  • React (Vite/CRA)、Angular、Vue CLI、Next.js、Nuxt – 生产构建通常会自动生成内容哈希的文件名。请确认 dist/build 输出中包含哈希,并使用长期不变的 immutable 头部来提供这些资源。
  • SSR 框架(Next.js、Nuxt) – 让框架自行管理资源哈希。确保 HTML 响应带有重新验证头部或使用带 must-revalidate 的短 CDN TTL。对于动态页面,如可行,应返回 ETagLast‑Modified
  • 单页应用(SPA) – 始终重新验证 index.html。对于深层链接,使用相同的头部为应用路由提供 index.html

CDN 最佳实践

  1. 让你的源服务器发送上述头部;大多数 CDN 都会遵守它们。
  2. 部署后使 HTML 路由失效/清除,以便更新的入口点能够快速可见。
  3. 对于带哈希的资源,你通常不需要清除 JS/CSS,因为新构建会使用新文件名。
  4. 为 HTML 设置较短的 CDN TTL(例如 60–300 秒),以防止清除遗漏时作为安全网。
  5. 在 CDN(以及源服务器)上保留旧的带哈希资产一段时间,以避免仍在引用先前构建的用户出现 404,并支持回滚。

API 与数据新鲜度

  • 使用 ETagLast‑ModifiedCache-Control: no-cache(或短 max-age + must-revalidate)。这可以避免陈旧数据,并通过 304 响应保持低带宽。
  • 对于高度动态或敏感且绝不能重复使用的响应,请使用 Cache-Control: no-store

Service workers(可选,高级)

  • Service workers 让你能够脚本化缓存和离线行为。

  • 为缓存设定版本(例如 app-cache-v42),并在安装时预缓存资源,以实现可预测的离线行为。

  • 每次部署时,发布一个新的 service worker。决定你的更新用户体验(UX):

    1. 当有可用更新时提示用户刷新(控制力强,清晰)。
    2. 使用 self.skipWaiting()clients.claim() 自动激活(更快,但需考虑 UX 权衡)。
  • 绝不要让 service worker 永久提供陈旧的 HTML。 对 HTML 使用 network‑firststale‑while‑revalidate 策略,以便及时发现更新。

如何强制重新验证

  • 针对您自己: 硬刷新 (Ctrl/Cmd+Shift+R) 或在 DevTools 中启用 “Disable cache”。

  • 针对所有用户:

    1. 保持 HTML 为 no-cache, must-revalidate 并返回 ETagLast‑Modified
    2. 在部署后立即使 HTML 路由的 CDN 缓存失效。
    3. 在紧急情况下,临时在 HTML 上设置 Cache-Control: no-store 以强制刷新,然后恢复。

如何验证您的设置

浏览器开发者工具 → 网络

  • index.html 在重新加载后应显示 200304(而不是 “from cache”),表示已重新验证。
  • 带哈希的 JS/CSS 通常在部署之间会显示 “from disk cache” 或 “from memory cache”。

curl 检查

# Get headers (look for ETag)
curl -I https://your.site/index.html

# Conditional request – expect 304 if unchanged
curl -H "If-None-Match: <etag-value>" -I https://your.site/index.html

常见陷阱

  • 跳过内容哈希 → 导致文件陈旧并且清除缓存变得复杂。始终使用带哈希的文件名进行缓存破坏(cache‑busting)。
  • 对 HTML 设置过于激进的 max-age → 会阻止更新及时到达用户。
  • 部署后忘记清除 CDN 上的 HTML → 用户可能仍然收到旧的入口文件。
  • 对可能在不更改文件名的情况下变化的资源错误地使用 immutable → 浏览器将永远不会重新验证这些资源。

缓存破坏 & 长期缓存指南

查询字符串缓存破坏

  • file.js?v=123 – 某些缓存会忽略查询参数。
  • 建议 使用内容哈希文件名(例如 file.1a2b3c.js)。

HTML 的长期缓存

  • HTML 必须在每次请求时重新验证;否则用户将看不到新构建。

立即移除旧资产

  • 使用旧 HTML 页面的用户仍可能请求之前哈希的文件。
  • 保留 最近构建的资产一段安全时间窗口(例如 24 小时)。

Service‑worker 陷阱

  • 永久提供陈旧 HTML 的 Service Worker 会导致更新失效。
  • 确保 HTML 重新验证,并制定明确的更新策略(版本升级、提示、自动激活等)。

Security & Privacy Notes

  • Never cache sensitive or private data. Use Cache-Control: no-store for such responses.
  • When using third‑party CDNs, plugins, or service‑worker libraries, verify they meet your organization’s security and compliance requirements.
  • At Oracle: confirm alignment with internal guidelines before adopting external tools.

简单部署清单

  1. 构建

    • 所有 静态资源输出内容哈希的文件名。
  2. HTML 响应

    • Cache-Control: no-cache, must-revalidate
    • 包含 ETag Last‑Modified
  3. 静态资源

    • Cache-Control: public, max-age=31536000, immutable
  4. 发布顺序

    • 上传新的哈希资源。
    • 然后发布更新后的 HTML。
  5. CDN

    • 部署后使 HTML 路由的 CDN 缓存失效(推荐)。
  6. 回滚安全

    • 保留最近几次构建的资源,以便回滚和为延迟返回的用户提供资源。
  7. 服务工作线程(如使用)

    • 更新工作线程版本。
    • 实现更新提示或自动激活策略。

常见问题

QuestionAnswer
用户是否始终获取最新的构建?会的。HTML 会重新验证并指向带有新哈希的资源;已更改的资源会重新下载,未更改的资源则从缓存中复用。
如果只有 JS 发生了变化怎么办?HTML 会将 <script> 标签更新为新的哈希;重新验证会自动捕获该变化。
我需要让用户强制刷新吗?不需要——哈希加上正确的响应头会透明地处理更新。
“no‑cache” 会很慢吗?不会。使用 ETagLast‑Modified 时,浏览器通常会收到快速的 304 Not Modified 响应,并复用本地副本。
我可以跳过 CDN 清除吗?对于资源通常可以(它们已经带有哈希)。如需立即传播,可清除 HTML。

结束语

这种模式——重新验证的 HTML带有长期不可变缓存的哈希资源以及带验证器的 API——在最小的运维开销下提供快速加载和安全更新。

  1. 从上述基础开始。
  2. 在浏览器的 Network 面板中验证行为。
  3. 随着应用的增长进行迭代。

如果您计划使用第三方工具或 CDN,请确认它们符合贵组织的安全和隐私标准。

在 Oracle: 在采用外部工具之前,请确认符合内部指南。

Back to Blog

相关文章

阅读更多 »

当从 S3 提供图像不再足够好时

背景 我在7年前发布了我的第一篇博客文章。我在Medium上写了大约一年,然后创建了Ready, Set, Cloud。在其大部分生命周期中,该站点尚未…