iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
Modern Web

Learn HTTP With JS系列 第 28

HTTP caching (第一篇)

  • 分享至 

  • xImage
  •  

大綱

底下網羅關於 HTTP Caching, HTTP Conditional Request 的 Headers,會在接下來的段落陸續介紹到

Header Name Header Type Explain
Cache-Control Request/Response
Expires Response ❌ HTTP/1.0 就有的,逐漸被 Cache-Control 取代
Last-Modified Response 📗 Last-Modified: Sat, 12 Jul 2025 07:20:17 GMT
ETag Response
Vary Response 📗 Vary: Accept-Encoding, Origin會在第 30 篇文章介紹到
Pragma Request/Response ❌ Deprecated
Age Response 📗 Age: 24
If-Range Request 📗 If-Range: Strong ETag📗 If-Range: Last-Modified✅ Must be use with Range Request Header
If-Modified-Since Request 📗 If-Modified-Since: Last-Modified✅ Conditional Request,主要用來更新快取
If-None-Match Request 📗 If-None-Match: Strong ETag | Weak ETag👶 Weak Comparison✅ Conditional Request,主要用來更新快取✅ If-None-Match 的優先度 > If-Modified-Since
If-Unmodified-Since Request 📗 If-Modified-Since: Last-Modified✅ Conditional Request,主要用來更新資源
If-Match Request 📗 If-Match: Strong ETag💪 Strong Comparison✅ Conditional Request,主要用來更新資源✅ If-Match 的優先度 > If-Unmodified-Since

ETag

  • 全名是 Entity Tag
  • 語法
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" # Weak
ETag: "0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" # Strong

Weak ETag

  • 通常是把 file metadata 拿去 hash,方法沒有規定
  • 同樣的 Weak ETag,不能確保 file content 完全一致
  • 但用在 cache 優化很有效

看看 NodeJS etag 的實作,就是拿 lastModified檔案大小 去做 hash

function stattag(stat) {
  var mtime = stat.mtime.getTime().toString(16);
  var size = stat.size.toString(16);

  return '"' + size + "-" + mtime + '"';
}

Strong ETag

  • 通常是把 file content 拿去 hash,方法沒有規定
  • 同樣的 Strong ETag,可以確保 file content 完全一致
  • Strong ETag 的生成,效能比 Weak ETag 更差

看看 NodeJS etag 的實作,實作上也是非常樸實無華

function entitytag(entity) {
  if (entity.length === 0) {
    // fast-path empty
    return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
  }

  // compute hash of entity
  var hash = crypto
    .createHash("sha1")
    .update(entity, "utf8")
    .digest("base64")
    .substring(0, 27);

  // compute length of entity
  var len =
    typeof entity === "string"
      ? Buffer.byteLength(entity, "utf8")
      : entity.length;

  return '"' + len.toString(16) + "-" + hash + '"';
}

Cache-Control

  • 用來控制 cache 的各種行為
  • cache 存放位置(public, private)
  • 是否允許 cache(no-cache, no-store)
  • cache 有效期控制(max-age, s-maxage)
  • cache 過期後的行為 (must-revalidate, proxy-revalidate, stale-while-revalidate)
  • cache 可否被轉換(no-transform)
  • cache 優化策略(only-if-cached, immutable)
  • 其他很少用到的(must-understand)

Directives

Directive Request Response
max-age 📗 Cache-Control: max-age=600代表 Response 在生成後的 600 秒內都算 fresh
s-maxage (shared-maxage) - 📗 Cache-Control: s-maxage=600同 max-age優先度 > max-age
no-cache 同 ➡️ 可被 cache,但是每次都需跟 origin server 驗證
no-store 同 ➡️ 禁止任何形式的 cache
must-revalidate - Cache-Control: max-age=600, must-revalidate600 秒內可以使用 cache,超過的話就必須重新驗證
proxy-revalidate - 同 must-revalidate,for shared caches only
private - Response 只能被存在 private cache
public - Response 可被存在 shared cache
no-transform 同 ➡️ 禁止中間層把 response body 做轉換
immutable - response 在 fresh 期間不會異動
stale-while-revalidate - Cache-Control: max-age=600, stale-while-revalidate=300swr 套件的命名來源
must-understand - Cache-Control: must-understand, no-store必須了解 status code 的涵義,才可以 cache
only-if-cached client 只想拿 cache 的資料 -
stale-if-error 主流瀏覽器不支援 -
max-stale 主流瀏覽器不支援 -
min-fresh 主流瀏覽器不支援 -

小插曲,觀察 Chrome Disable Cache 的行為

隨便打開一個網頁,F12 > Network > Disable Cache 打勾,實際發送的是 Cache-Control: no-cache

chrome-disable-cache-no-cache

取消勾選,再重整網頁,實際發送的是 Cache-Control: max-age=0

chrome-enable-cache-max-age-0

Conditional Requests

  • If-* 開頭的 Request Headers
  • If-* 條件為 true,則執行對應的 HTTP Method 操作
  • If-Range + Range 用來發起 Conditional Range Request,true 回傳對應的 Range,false 回傳整個 resource
  • If-None-Match + If-Modified-Since 通常會一起使用,用來更新快取
  • If-Match + If-Unmodified-Since 通常會一起使用,用來更新資源,若 If-* 條件為 false,則回傳 412 Precondition Failed

小結

HTTP Caching 跟 HTTP Conditional Requests 是兩個密不可分的概念。在第一個篇章,我們先有一個概觀,把這個主題會用到的 Headers 都介紹過一輪,接下來我們就會進到實作的環節~

參考資料


上一篇
beyond CORS
下一篇
HTTP caching (第二篇)
系列文
Learn HTTP With JS30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言