ข้ามไปเนื้อหาหลัก

Category: reference

HTTP Caching — Cache-Control, ETag, Stale-While-Revalidate

ทำความเข้าใจ HTTP caching headers ที่ถูกต้อง ลด server load และเร็วขึ้นสำหรับผู้ใช้กลับมา

· อ่านประมาณ 3 นาที

สารบัญ

ทำไม Caching สำคัญ

Response ที่ cache ได้ = ไม่ต้อง round trip ไปหา server ทุกครั้ง:

  • ลด latency ผู้ใช้รับ response จาก cache (local/CDN) แทน origin server
  • ลด bandwidth ไม่ส่งข้อมูลซ้ำที่ยังไม่เปลี่ยน
  • ลด server load origin server ตอบน้อยลง

Cache-Control Header

Header หลักที่ควบคุม caching behavior:

Cache-Control: max-age=3600

Directives ที่สำคัญ

Directiveความหมาย
max-age=Ncache ได้ N วินาที
no-cachecache ได้ แต่ต้อง revalidate กับ server ก่อนใช้
no-storeห้าม cache เลย (sensitive data)
publiccache ได้โดย browser และ CDN
privatecache ได้เฉพาะ browser ห้าม CDN
immutableบอกว่า resource จะไม่เปลี่ยนตลอด max-age
stale-while-revalidate=Nใช้ cache เก่าได้ระหว่าง revalidate ใน background
must-revalidateต้อง revalidate เมื่อ stale

Pattern: Static Assets (JS/CSS/Images)

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

365 วัน + immutable — เหมาะกับไฟล์ที่มี content hash ในชื่อ เช่น:

  • main.abc123.js
  • style.def456.css

เมื่อ content เปลี่ยน filename ก็เปลี่ยน (cache bust อัตโนมัติ) ดังนั้น immutable บอกว่าไฟล์ชื่อนี้ไม่มีทางเปลี่ยน ไม่ต้อง revalidate


Pattern: HTML Files

Cache-Control: no-cache

no-cache — browser เก็บ cache ได้แต่ต้อง check กับ server ก่อนใช้เสมอ browser ส่ง If-None-Match หรือ If-Modified-Since — ถ้า server ตอบ 304 Not Modified browser ใช้ cache ได้โดยไม่ต้อง download ซ้ำ


ETag (Entity Tag)

Server ส่ง ETag = fingerprint ของ content:

HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: no-cache

Browser ส่งกลับใน request ถัดไป:

GET /page.html
If-None-Match: "abc123"

Server ตอบ:

  • 304 Not Modified (content ยังเหมือนเดิม) — browser ใช้ cache
  • 200 OK + content ใหม่ (content เปลี่ยน) — browser อัปเดต cache

ประหยัดแค่ bandwidth ไม่ใช่ round trip — แต่ก็ดีกว่าไม่มีอะไรเลย


Last-Modified

ทางเลือก ETag ที่ใช้เวลาแทน fingerprint:

Last-Modified: Wed, 14 Jun 2026 08:00:00 GMT
Cache-Control: no-cache

Browser ส่ง:

If-Modified-Since: Wed, 14 Jun 2026 08:00:00 GMT

Server ตอบ 304 ถ้า resource ไม่เปลี่ยนหลังจากเวลานั้น ใช้ ETag ถ้าเลือกได้ เพราะ accurate กว่า (second-precision vs content-level)


Stale-While-Revalidate

Pattern ที่ดีมากสำหรับ API ที่ข้อมูลไม่ต้อง real-time:

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

ความหมาย:

  • 0–60 วินาที: ใช้ cache ได้เลย (fresh)
  • 60–86460 วินาที: ใช้ cache เก่า (stale) แต่ fetch ใหม่ใน background
  • หลัง 86460 วินาที: ต้องรอ fresh response

ผลลัพธ์: ผู้ใช้เห็น response เร็วเสมอ (ไม่มี loading ให้รอ) และข้อมูล fresh ภายใน request ถัดไป


Vary Header

บอก cache ว่า response อาจต่างกันตาม request header:

Vary: Accept-Encoding

Cache ต้องเก็บ response แยกกันตาม Accept-Encoding — ไม่ serve gzip content ให้ client ที่ไม่รองรับ

Vary: Accept-Language

ระวัง Vary: * — บอกว่าห้าม cache shared proxy เลย


CDN vs Browser Cache

Browser CacheCDN Cache
ที่เก็บเครื่องผู้ใช้Edge server ใกล้ผู้ใช้
ควบคุมด้วยCache-Control: private / publicCache-Control: public, s-maxage
Invalidateผู้ใช้ clear cache เองPurge API ของ CDN
ใช้ s-maxageไม่อ่านอ่าน (override max-age)
Cache-Control: public, max-age=300, s-maxage=86400

Browser cache 5 นาที, CDN cache 24 ชั่วโมง — CDN รีเฟรชบ่อยกว่าเพราะ invalidate ผ่าน API ได้


Cache Busting Strategies

Content Hash (แนะนำ)

/assets/main.d3f4a9b.js   → เปลี่ยน content = เปลี่ยนชื่อไฟล์

Query String

/style.css?v=2.1.0        → ง่าย แต่บาง proxy ไม่ cache URL ที่มี query string

Cache-Control: no-cache + ETag

HTML → no-cache (check ก่อน)
Assets → immutable (ไม่ต้อง check)

ตรวจสอบ Cache ด้วย DevTools

Chrome DevTools → Network tab:

  • Size column: “(memory cache)” = จาก browser memory, “(disk cache)” = จาก disk
  • Status 304: revalidated — ใช้ cache
  • Status 200 (from cache): ใช้ cache โดยตรง ไม่ส่ง request
  • กด Ctrl+Shift+R หรือ disable cache checkbox เพื่อ bypass

Headers ที่ไม่ควรใช้

/* เก่า ไม่ใช้แล้ว */
Pragma: no-cache
Expires: 0

/* ใช้ Cache-Control แทน */
Cache-Control: no-cache

Expires ยังรองรับ แต่ Cache-Control: max-age ดีกว่า เพราะไม่ขึ้นกับ clock sync ระหว่าง client และ server


Quick Reference

Resource Typeแนะนำ
HTML (homepage, listings)no-cache
HTML (article ที่ไม่ค่อยเปลี่ยน)max-age=3600, stale-while-revalidate=86400
JS/CSS (hashed filename)max-age=31536000, immutable
Images (hashed)max-age=31536000, immutable
Fontsmax-age=31536000, immutable
API (real-time)no-store
API (semi-static)max-age=60, stale-while-revalidate=600
RSS/Sitemapmax-age=1800