← 中文资源

简体中文版本

用内容哈希为静态资源做长期缓存

为资源文件名加入内容哈希并启用 immutable 缓存,HTML 保持每次校验,同时校验部署中不存在失效引用。

阅读英文原文

概述

永远不会变化的 URL 无法长期缓存,因为它背后的内容确实会变化。内容哈希把 URL 变成字节的函数:文件变化时文件名随之变化,旧 URL 可以永久缓存。于是 HTML 成为唯一需要每次校验的文档。正是这种拆分,让部署对用户即时生效,同时允许静态资源缓存一年。

步骤

  1. 让构建工具为每个资源生成带哈希的文件名:
export default {
  build: {
    rollupOptions: {
      output: {
        entryFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash][extname]',
      },
    },
  },
};
  1. 为哈希资源与 HTML 设置不同的缓存策略:
/assets/*
  Cache-Control: public, max-age=31536000, immutable

/index.html
  Cache-Control: no-cache
  1. 验证边缘实际返回的响应头:
curl -sI https://app.example.com/assets/app.9f2c1a.js | grep -i cache-control
curl -sI https://app.example.com/ | grep -i cache-control
  1. 校验 HTML 引用的文件都存在于本次部署:
grep -o '/assets/[^"]*' dist/index.html | while read -r f; do
  test -f "dist$f" || echo "missing $f"
done

验证

哈希资源应返回 max-age=31536000, immutable,HTML 应返回 no-cache。构建后 HTML 中的每个引用都应存在于 dist,否则构建失败而不是上线一个缺样式的页面。发布新版本后强制刷新应看到新的哈希文件名,而旧文件在缓存窗口内仍可访问,保证仍持有旧 HTML 的浏览器不会 404。

注意事项

  • 删除旧哈希文件会让回滚和尚未更新的浏览器失效,应保留至少一个缓存周期。
  • 不要在同时包含非哈希文件的路径前缀上应用 immutable。
  • Service Worker 拥有独立缓存,需要与 CDN 一起版本化。
  • 先上传资源再上传 HTML,避免用户拿到引用不存在文件的页面。
  • 回滚时应重新部署旧 HTML,而不是假设旧资源仍然存在。