HTTP 快取完全指南:強制快取、協商快取與 CDN 快取策略全解析

Web/SEO

HTTP 快取:效能優化的第一道防線

合理設定快取,可以讓二次造訪零網路請求,載入時間從秒級降到毫秒級。

快取類型 機制 是否請求伺服器 速度
強制快取 Cache-Control / Expires 否 最快(0ms)
協商快取 ETag / Last-Modified 是(條件請求) 快(304 回應)
無快取 no-cache / no-store 是(完整請求) 最慢

一、強制快取

Cache-Control 指令

Cache-Control: max-age=31536000, immutable
指令 作用 適用場景
max-age=N 快取 N 秒 靜態資源
s-maxage=N CDN 快取 N 秒(覆蓋 max-age) CDN 場景
immutable 即使過期也不重新驗證 含雜湊值的檔名
public 允許中間快取 CDN
private 僅瀏覽器快取 使用者個人化內容
no-cache 每次使用前必須驗證 HTML 頁面
no-store 完全不快取 敏感資料
must-revalidate 過期後必須驗證 API 回應
stale-while-revalidate=N 過期後先回傳舊值,背景更新 漸進更新

Expires(舊標準)

Expires: Thu, 02 Jun 2027 00:00:00 GMT

優先順序:Cache-Control: max-age > Expires。


二、協商快取

ETag / If-None-Match

# 首次請求
GET /api/data
→ 200 OK
  ETag: "abc123"
  Cache-Control: max-age=0

# 再次請求(附條件)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified  (無回應主體,節省頻寬)

Last-Modified / If-Modified-Since

# 首次請求
GET /style.css
→ 200 OK
  Last-Modified: Mon, 01 Jun 2026 12:00:00 GMT

# 再次請求
GET /style.css
If-Modified-Since: Mon, 01 Jun 2026 12:00:00 GMT
→ 304 Not Modified

ETag vs Last-Modified

維度 ETag Last-Modified
精度 位元組級 秒級
效能 需計算雜湊 直接讀取檔案時間
可靠性 高 低(1 秒內多次修改會遺失)
分散式 需保證雜湊一致 天然一致

建議:優先使用 ETag。


三、快取策略矩陣

HTML 頁面

Cache-Control: no-cache

HTML 是入口檔案,必須每次驗證,確保使用者拿到最新版本。

含雜湊值的靜態資源

Cache-Control: max-age=31536000, immutable

檔名含雜湊值(如 app.abc123.js),內容變化時雜湊值變化 → URL 變化 → 新請求。舊 URL 永遠不會變,可以快取一年並標記 immutable。

不含雜湊值的靜態資源

Cache-Control: max-age=86400, stale-while-revalidate=604800

過期後先回傳舊值,背景靜默更新,使用者無感知。

API 回應

Cache-Control: private, max-age=0, must-revalidate

API 回應通常含使用者資料,private 禁止 CDN 快取,must-revalidate 確保過期後不回傳舊值。


四、Vary 標頭:快取鍵的擴展

Vary: Accept-Encoding, Accept-Language

Vary 告訴快取:除了 URL,還要根據這些請求標頭的值區分快取版本。

Vary 值 含義 場景
Accept-Encoding 按壓縮方式快取 gzip vs br
Accept-Language 按語言快取 多語言網站
Cookie 按 Cookie 快取 個人化頁面
* 每個請求獨立快取 等於不快取

注意:Vary: Cookie 會導致快取命中率極低,慎用。


五、CDN 快取策略

回源控制

# CDN 快取 1 天,瀏覽器快取 5 分鐘
Cache-Control: public, s-maxage=86400, max-age=300
場景 s-maxage max-age 說明
靜態資源 31536000 31536000 CDN 和瀏覽器都長期快取
API 回應 300 0 CDN 快取 5 分鐘,瀏覽器不快取
HTML 60 0 CDN 快取 1 分鐘,瀏覽器不快取
個人化內容 — 0 CDN 不快取,瀏覽器不快取

CDN 快取重新整理

方式 速度 適用
URL 預熱 快 新內容上線
全量重新整理 慢 緊急修復
目錄重新整理 中 批次更新
Cache-Control: no-cache 自動 API 場景

Stale-While-Revalidate 實戰

Cache-Control: max-age=60, stale-while-revalidate=86400
第 0 秒:請求 → 快取 MISS → 回源 → 回傳新鮮值
第 30 秒:請求 → 快取 HIT → 回傳快取值
第 90 秒:請求 → 快取 STALE → 立即回傳舊值 + 背景回源
第 91 秒:背景回源完成 → 快取更新
第 120 秒:請求 → 快取 HIT → 回傳新值

效果:使用者永遠不等待回源,背景靜默更新。


六、快取破壞策略

內容雜湊(推薦)

<!-- 建置時生成雜湊值 -->
<script src="/app.abc123.js"></script>
<link href="/style.def456.css">
// webpack 設定
output: {
  filename: '[name].[contenthash:8].js',
}

版本號查詢參數

<!-- 簡單但 CDN 可能不快取 ?v= 參數 -->
<script src="/app.js?v=2.0.0"></script>

問題:某些 CDN 忽略查詢參數,導致快取不更新。

時間戳(不推薦)

<!-- 每次都變,完全破壞快取 -->
<script src="/app.js?t=1717315200"></script>

七、Service Worker 快取

Cache First 策略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
  );
});

Stale While Revalidate 策略

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('dynamic').then(cache =>
      cache.match(event.request).then(cached => {
        const fetchPromise = fetch(event.request)
          .then(response => {
            cache.put(event.request, response.clone());
            return response;
          });
        return cached || fetchPromise;
      })
    )
  );
});

快取策略選擇

策略 優先來源 適用
Cache First 快取 字型、圖片
Network First 網路 API、HTML
Stale While Revalidate 快取+背景更新 CSS、JS
Network Only 網路 非 GET 請求
Cache Only 快取 離線頁面

八、偵錯快取

Chrome DevTools

  1. DevTools → Network 面板
  2. 查看 Size 欄:
    • from disk cache:磁碟快取命中
    • from memory cache:記憶體快取命中
    • 實際大小:未命中
  3. 查看 Age 回應標頭:快取已存在的時間
  4. 勾選 Disable cache:停用快取(DevTools 開啟時)

curl 驗證

# 查看回應標頭
curl -I https://example.com/style.css

# 送出條件請求
curl -I -H 'If-None-Match: "abc123"' https://example.com/style.css
# 304 表示協商快取命中

# 模擬不同 Accept-Encoding
curl -I -H 'Accept-Encoding: br' https://example.com/style.css

九、最佳實踐清單

  1. HTML:no-cache,確保入口始終最新
  2. 含雜湊值的 JS/CSS:max-age=1 年 + immutable
  3. API:must-revalidate,過期後必須驗證
  4. CDN:s-maxage 控制回源頻率
  5. Vary: Accept-Encoding,區分壓縮方式
  6. 使用 ETag 而非 Last-Modified
  7. stale-while-revalidate 實現無感更新
  8. Service Worker 補充離線能力
#HTTP缓存#Cache-Control#ETag#CDN#性能优化