Developer Tools

HTTP 缓存指南:无需额外基础设施即可加速您的网站

掌握 Cache-Control 头部、ETag、CDN 缓存策略和浏览器缓存行为,大幅提升网站加载速度并降低服务器成本。

7分钟阅读

数据中心的服务器机架

缓存是您能做的杠杆效益最高的性能优化手段。缓存的响应在微秒内即可送达,零成本,且无需任何服务器处理。然而,大多数应用要么缓存过于激进(提供过期内容),要么完全不缓存(浪费带宽和计算资源)。理解 HTTP 缓存,能让它从 bug 的温床变成你的超级武器。

HTTP 缓存的工作原理

当浏览器或 CDN 接收到响应时,它会检查响应头来决定是否缓存以及缓存多长时间。下次请求同一资源时,可以直接提供缓存副本——完全不需要访问您的服务器。

缓存生命周期分为两个阶段:

  1. 新鲜度(Freshness) — 缓存副本是否仍然有效?由 Cache-Control: max-ageExpires 决定。
  2. 验证(Validation) — 若已过期,能否向服务器确认内容未发生变化?由 ETagLast-Modified 决定。

Cache-Control:主要缓存指令

Cache-Control 是最强大的缓存头部,是一组以逗号分隔的指令:

Cache-Control: public, max-age=31536000, immutable

关键指令

指令 含义
public 任何缓存(浏览器、CDN、代理)均可存储
private 仅终端用户的浏览器可缓存(CDN 不可缓存)
no-cache 每次使用前必须向服务器重新验证(不是"不缓存")
no-store 完全不缓存——用于敏感数据
max-age=N 缓存 N 秒
s-maxage=N CDN 专用最大缓存时长(对共享缓存覆盖 max-age
immutable 内容永不变更——完全跳过重新验证
must-revalidate 过期后必须重新验证才能提供
stale-while-revalidate=N 在后台获取新内容期间,可继续提供过期内容最多 N 秒

⚠️ no-cache 并不意味着"不缓存"。它的意思是"缓存它,但每次使用前都要检查是否仍然有效"。如果您真的不希望内容被缓存,请使用 no-store

按资源类型制定缓存策略

不同资源需要不同的缓存策略:

带内容哈希的静态资源(CSS、JS、图片)

Cache-Control: public, max-age=31536000, immutable

如果您的构建工具在文件名中加入了哈希值(main.a3f9b2c.js),内容变化时 URL 也会随之改变。永久缓存——任何新版本都会有新的 URL。

HTML 页面

Cache-Control: no-cache

或设置较短的 TTL:

Cache-Control: public, max-age=60, stale-while-revalidate=3600

HTML 变化频繁,且链接到带哈希的资源。建议短暂缓存或强制重新验证。

API 响应

# 公开数据(如产品目录)
Cache-Control: public, max-age=300, stale-while-revalidate=600

# 用户专属数据
Cache-Control: private, max-age=60

# 实时数据(股票价格、实时比分)
Cache-Control: no-store

敏感数据(身份验证、支付)

Cache-Control: no-store

绝对不要缓存。

ETag 与条件请求

当缓存响应过期时,浏览器不会直接丢弃它——而是询问服务器内容是否仍然有效。这就是重新验证

ETag

ETag 是响应内容的指纹:

# 服务器发送:
ETag: "a3f9b2c8d4e1"

# 浏览器下次请求:
If-None-Match: "a3f9b2c8d4e1"

# 若内容未变,服务器响应:
HTTP/1.1 304 Not Modified
(无响应体——节省带宽)

# 若内容已变,服务器响应:
HTTP/1.1 200 OK
ETag: "b7c2d4e9a1f3"
(完整的新响应)

304 Not Modified 响应没有响应体——只有头部。这节省了重新下载内容所需的全部带宽。

Last-Modified

类似的机制,但使用时间戳而非哈希值:

Last-Modified: Tue, 01 Apr 2026 10:00:00 GMT

# 浏览器发送:
If-Modified-Since: Tue, 01 Apr 2026 10:00:00 GMT

ETag 更为可靠(在负载均衡服务器场景下,时间戳可能不够精确)。

Vary 头部:按请求变体分别缓存

Vary 头部告知缓存哪些请求头会影响响应内容:

Vary: Accept-Encoding

这会分别缓存 gzipbr 压缩的响应。常见用法:

Vary: Accept-Encoding          # 分别缓存压缩/未压缩版本
Vary: Accept-Language          # 按语言分别缓存
Vary: Accept                   # 分别缓存 JSON 和 HTML 响应

⚠️ Vary: CookieVary: Authorization 实际上会禁用 CDN 缓存——CDN 无法缓存用户专属的响应。

stale-while-revalidate:后台刷新

这是最实用的现代缓存模式之一:

Cache-Control: max-age=60, stale-while-revalidate=600
  • 前 60 秒内直接从缓存即时响应(新鲜状态)
  • 第 60 到 660 秒之间:立即提供过期缓存副本,同时在后台获取新版本
  • 超过 660 秒后:必须重新验证后才能提供

用户始终能获得快速响应,缓存保持新鲜,且不会强迫任何人等待网络往返。

CDN 缓存注意事项

CDN(Cloudflare、CloudFront、Fastly)遵循 Cache-Control 头部,但也引入了自身的复杂性:

  • s-maxage 允许您为 CDN 和浏览器设置不同的 TTL:

    Cache-Control: public, max-age=60, s-maxage=86400
    

    浏览器缓存 1 分钟;CDN 缓存 24 小时。

  • 缓存清除 — 部署时,清除 CDN 中已更新资源的缓存。大多数 CDN 提供基于 API 的缓存清除功能。

  • 缓存键 — CDN 基于 URL + Vary 头部进行缓存。查询字符串通常包含在缓存键中。

测试缓存行为

使用我们的 API Request Builder 检查响应头部,验证您的缓存配置是否正常工作:

  1. 发送请求并检查 Cache-ControlETagLast-Modified 头部
  2. 再次发送相同请求——检查 Age 头部(自缓存以来的秒数)和 X-Cache: HIT
  3. 检查 CF-Cache-Status(Cloudflare)或 X-Cache(CloudFront)以确认 CDN 缓存生效

在 Chrome DevTools → Network 标签页 → 点击某个资源 → Headers 标签页——在响应中查找 from disk cachefrom memory cache

Nginx 缓存配置

在 Nginx 层面配置缓存头部,确保行为一致:

# 静态资源——永久缓存
location ~* \.(js|css|woff2|png|jpg|webp|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# HTML——始终重新验证
location ~* \.html$ {
    add_header Cache-Control "no-cache";
}

# API——短期缓存加 stale-while-revalidate
location /api/ {
    add_header Cache-Control "public, max-age=60, stale-while-revalidate=600";
}

使用我们的 Nginx Config Generator 为您的使用场景生成完整、优化的 Nginx 配置。

缓存检查清单

  • 静态资源(CSS/JS)使用内容哈希文件名 + max-age=31536000, immutable
  • HTML 使用 no-cache 或非常短的 max-age
  • API 响应根据更新频率进行缓存
  • 敏感数据使用 no-store
  • 已启用 ETag 或 Last-Modified 以支持条件请求
  • 在适当的 API 端点上使用 stale-while-revalidate
  • CDN 缓存头部已验证并测试

正确配置 HTTP 缓存,能让回访用户感受到即时响应,降低带宽成本,减轻服务器负载——这一切都无需任何额外基础设施。