把一篇文章改好了标题、补好了内鏈,發布之後自己看着没問题,但同事打開還是老样子,搜尋引擎抓到的也可能是几天前的版本。這種“發布成功、頁面没變”的情况,多數不是發布失敗,而是缓存层還在拿舊副本。做站点运营的人,需要知道自己的站点到底有几层缓存,以及每一层什么时候會更新。
先搞清楚有哪几层缓存
一個訪客看到的頁面,通常會经過這几道關口:
- 浏览器缓存:訪客本地儲存的副本,由响應头决定它多久不回来問服務器。
- CDN 邊缘节点缓存:离訪客最近的机房儲存的副本,命中时不回源。
- 服務端缓存:頁面缓存插件、反向代理、對象缓存等,在源站内部再存一层。
- 資料库查询缓存:某些 CMS 會把查询结果暂存,内容更新後需要主動清理。
排查时按“离訪客最近的一层”往回走,比盲目点一遍“清除缓存”更有效。
用响應头判断命中了哪一层
同样的地址连續請求两次,對比返回头就能看出不少信息。重点看這几項:
- Cache-Control:max-age 有多長,是否带 s-maxage、stale-while-revalidate。
- Age:副本已经在邊缘放了多久,數字很大說明命中缓存。
- X-Cache、CF-Cache-Status 之類的头:HIT、MISS、EXPIRED、DYNAMIC 直接告诉你结果。
- Last-Modified、ETag:用于协商缓存,内容變了這两個值應该跟着變。
- Vary:如果带了 Cookie 或 User-Agent,缓存會按维度分片,命中率明顯下降。
如果更新内容之後 Age 一直不清零,說明邊缘缓存没有被刷新;如果 Age 很小但内容還是舊的,問题更可能在源站那一层。
HTML 和静態资源應该用不同策略
一刀切的缓存設定往往两头不讨好:设短了,静態资源反复回源;设長了,文章改完迟迟不更新。比較常见的做法是分開處理。
- HTML 頁面:設定較短的 max-age,或者走协商缓存,让浏览器每次都回来確認一次。
- 带指纹的 CSS、JS:文件名里带哈希或版本号,可以放心用一年以上的長缓存和 immutable。
- 图片、字体:同样适合長缓存,但替換素材时要換文件名或加參數,不要直接覆盖原文件。
- 带登入態或個性化的頁面:不要做公共缓存,或者明确声明 private。
另外,栏目頁、标簽頁、搜尋頁這些容易被频繁訪問又變化較快的地址,要單獨看一眼它們的缓存策略,別和文章頁混在一起配置。
缓存键和 URL 參數容易被忽略
带追踪參數的分享連結、排序篩選參數,如果全部当作獨立的缓存键,會在邊缘留下大量内容几乎相同、只差几個字符的副本。這既占用存储,也让刷新變得麻烦。可以配置忽略不影响内容的參數,只保留真正决定頁面内容的字段。同时確認站点没有把同一篇内容暴露成多個可缓存的地址,否則刷新时容易漏掉其中一部分。
更新内容後的刷新與驗證流程
把刷新動作固定成流程,比每次凭感觉操作更稳:
- 先在源站直接確認内容已经改好,回源請求拿到的是新版本。
- 按具体 URL 精确刷新,而不是一上来就全量刷新。
- 如果改的是模板或公共区块,考虑目錄級刷新。
- 刷新完成後预热几個主要入口頁,避免第一批訪客回源压力過大。
- 用無痕窗口或另一台设备再打開一次,排除本地浏览器缓存的干扰。
顺带检查几個容易漏的地址:XML sitemap、RSS 或 feed、栏目的第一頁,以及之前被分享出去的老連結。這些地方往往挂在同一個域名下,却用了不同的缓存規則。
纳入日常巡检的几項
- 發布重要更新後,確認目标頁面的 Age 與内容版本一致。
- 抽查首頁、栏目頁、文章頁三類模板的响應头,確認設定没有被改回預設。
- 確認静態资源更新後文件名或版本号确實變了。
- 關注缓存命中率的變化,突然下降通常意味着參數或規則出了問题。
- 记錄每次全量刷新的時間和原因,便于事後對比流量與回源日誌。
缓存本身是為了让站点更快,不是為了让人猜不透。把“改完内容、刷新、驗證”這三步寫進發布清單,多數“頁面更新了但別人看到還是舊的”的問题,都能在几分钟内定位到是哪一层没跟上。