---
title: 缓存不是'加一层'：静态站点该缓存什么、缓存多久
url: https://vabora.dev/posts/caching-strategy-static-site
author: Vabora
published: 2026-08-22 09:30:00
updated: 2026-09-27 10:33:42
category: 技术分享
tags: 架构设计, 缓存, SEO
canonical: https://vabora.dev/posts/caching-strategy-static-site
license: 转载与引用请注明出处并保留原文链接
---

# 缓存不是"加一层"：静态站点该缓存什么、缓存多久

> 缓存做错比不做更糟：改完内容前台不变、登录后被缓存串号、CDN 里躺着半年前的页面。这篇讲清内容站的三层缓存策略与失效方式。
## 背景

"加个缓存"是性能优化里最容易被随口说出、也最容易埋雷的建议。内容站尤其明显：

- 缓存 HTML → 后台改完文章，前台还是旧的，客户以为你没保存；
- 缓存带会话的响应 → 换个浏览器就是别人的登录态（严重事故）；
- 静态资源没指纹却缓存一年 → 改了 CSS 用户永远看不到新版。

所以问题不是"要不要缓存"，而是**哪一层、缓存多久、怎么失效**。

## 做法：三层分开看

### 1. 页面 HTML：不缓存，或极短

前台是服务端渲染，一次请求的主要成本是模板渲染 + 若干次查询（本站在毫秒级）。这种量级下缓存 HTML 的收益远小于它带来的"内容不更新"困惑。

结论：**HTML 设 `no-cache` 或 `max-age=60`**，靠单页查询优化解决性能，而不是靠缓存糊住。

```text
Cache-Control: no-cache           # 允许浏览器存，但每次校验
```

### 2. 静态资源：内容指纹 + 一年

CSS/JS 用带内容指纹的文件名（`site.a1b2c3.css`），于是可以放心长缓存：

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

配合 nginx：

```nginx
location ~* \.(css|js|woff2|png|jpg|webp|svg)$ {
    expires 1y;
    add_header Cache-Control "public, max-age=31536000, immutable";
    access_log off;
}
```

注意：**图片要分类**。素材库里用户上传的图片路径不变（覆盖上传会变内容），所以图片缓存设 30 天而不是一年，避免"换了图还是旧的"。

### 3. 数据层：分内容定 TTL

站内的小块数据（标签云、站点统计、llms.txt）适合进程内文件缓存，但要**按变化频率**给不同 TTL：

| 数据 | TTL | 理由 |
|---|---|---|
| 标签/分类列表 | 10 分钟 | 变化少，出现位置多 |
| 首页统计数字 | 5 分钟 | 每次访问都算成本高 |
| llms.txt / sitemap | 1 分钟 | 要让新文章尽快被抓 |
| 访问统计写入 | 不缓存 | 写操作必须落库 |

并且所有写操作（保存文章、改设置）都要**主动清掉相关缓存**——TTL 是兜底，不是主要失效手段。

## 排查：改了内容前台没变

按这个顺序查，基本一次到底：

1. **先看 HTML**：`curl -sI https://站点/posts/xxx | grep -i cache`，如果 `no-cache` 就不是 HTML 缓存的问题；
2. **再看应用缓存**：`php vabora cache:clear` 后刷新，变了就是应用层 TTL 太长；
3. **再看 CDN**：CDN 的缓存策略常和源站不一致，且要单独刷新；
4. **最后看浏览器**：`Ctrl+Shift+R` 强制刷新，或换隐身窗口排除本地。

第 3 步是最容易被忽略的：源站明明对，用户在别的城市看到旧页面，问题在 CDN 边缘节点。

## 取舍

- **不上页面级全站缓存**：省下的那点 CPU 换来"内容一致性的复杂度"，对流量不大的站点是亏的。真到需要时，应该先做查询与模板层优化。
- **不做 `stale-while-revalidate` 的复杂组合**：语义好，但排障时"为什么我看到的和别人不一样"会变得难以解释；先保证确定性。
- **运营内容与代码分开失效**：改样式只让 CSS 指纹变，不需要刷 CDN 的 HTML；改内容只影响 HTML，不动资源缓存。
