Sebelumnya di "Caching Browser & CDN" kamu belajar gambaran besar. Di sini kita masuk ke detail header HTTP: directive Cache-Control yang sering membingungkan, alur validasi dengan ETag, dan bagaimana memilih strategi yang tepat untuk setiap jenis konten.
Hirarki cache — siapa saja yang menyimpan:
[Origin Server]
↑
[Reverse Proxy / CDN Edge] ← shared cache
↑
[Corporate / ISP Proxy] ← shared cache
↑
[Browser Cache] ← private cache (per user)
↑
[User]
Setiap lapisan bisa menyimpan response — selama header mengizinkan. Ini penting karena public vs private menentukan lapisan mana yang boleh cache.
Cache-Control directive lengkap:
| Directive | Arti |
|---|---|
public |
Semua cache boleh simpan (browser + CDN + proxy) |
private |
Hanya browser user yang boleh simpan — jangan CDN! (data personal) |
no-cache |
Boleh simpan, tapi wajib revalidasi sebelum dipakai |
no-store |
Jangan simpan sama sekali (data sensitif) |
max-age=N |
Fresh selama N detik sejak response diterima |
s-maxage=N |
Seperti max-age tapi khusus untuk shared cache (CDN) |
must-revalidate |
Setelah stale, wajib cek ke origin — tidak boleh serve stale |
proxy-revalidate |
Seperti must-revalidate tapi hanya untuk shared cache |
immutable |
File tidak akan pernah berubah — browser skip revalidasi |
stale-while-revalidate=N |
Boleh serve stale selama N detik sambil update di background |
stale-if-error=N |
Boleh serve stale selama N detik kalau origin error |
no-cache vs no-store — kebingungan klasik:
Cache-Control: no-cache ← simpan boleh, tapi validasi dulu tiap pakai
Cache-Control: no-store ← jangan simpan sama sekali
Gunakan no-store untuk halaman bank, data medis, token rahasia. Gunakan no-cache untuk HTML yang sering update tapi boleh pakai cache kalau belum berubah (hemat bandwidth via 304).
max-age vs s-maxage:
Cache-Control: public, max-age=60, s-maxage=3600
Browser cache 60 detik, CDN cache 1 jam. Trik umum: update HTML lebih sering di browser, biarkan CDN lebih agresif.
immutable — untuk asset yang versioned:
Cache-Control: public, max-age=31536000, immutable
File /assets/app.a1b2c3.js (hash di nama) pasti tidak berubah — kalau berubah, nama filenya juga berubah. immutable memberi tahu browser: "jangan bahkan pikirkan untuk cek ulang." Selama 1 tahun.
ETag + If-None-Match — validasi eksak:
ETag adalah "sidik jari" konten. Server membuat hash dari response body.
Request pertama:
GET /data.json
Response:
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: no-cache
Content-Length: 1500
{data besar...}
Browser menyimpan response + ETag. Berikutnya:
Request kedua:
GET /data.json
If-None-Match: "abc123"
Kalau ETag masih sama:
HTTP/1.1 304 Not Modified ← tanpa body, cepat!
Kalau ETag berubah:
HTTP/1.1 200 OK
ETag: "xyz789"
{data baru...}
Last-Modified + If-Modified-Since — fallback lama:
Alternatif ETag yang hanya cek timestamp:
Response:
Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT
Request berikut:
If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT
Response:
HTTP/1.1 304 Not Modified
ETag lebih akurat karena berbasis konten, bukan waktu. Kalau server punya replika di banyak mesin, timestamp bisa berbeda sedikit — ETag konsisten.
Vary — menghindari cross-contamination:
Cache-Control: public, max-age=3600
Vary: Accept-Encoding, Accept-Language
CDN akan menyimpan versi terpisah untuk kombinasi encoding + bahasa. Tanpa Vary, user yang minta Bahasa Indonesia bisa dapat response English yang ter-cache untuk user lain.
Header legacy (untuk kompatibilitas):
Expires: Wed, 21 Oct 2026 07:28:00 GMT ← HTTP/1.0, kalah dengan Cache-Control
Pragma: no-cache ← HTTP/1.0 equivalent no-cache
Kalau kamu menulis API baru, fokus ke Cache-Control. Expires dan Pragma cuma untuk client sangat lama.
Alur lengkap 304 Not Modified:
1. Browser ada cache stale (max-age habis)
2. Browser kirim: GET /data.json
If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct...
3. Server cek ETag dan/atau modified time
4a. Kalau sama → 304 Not Modified (0 byte body) → browser pakai cache
4b. Kalau beda → 200 OK + body baru → browser update cache
Manfaat: hemat bandwidth besar untuk file yang jarang berubah tapi harus divalidasi (HTML, JSON API).
Tabel pilihan strategi — kapan pakai apa:
| Tipe konten | Strategi yang disarankan |
|---|---|
JS/CSS/image dengan hash di nama (app.a1b2c3.js) |
public, max-age=31536000, immutable |
| HTML halaman | no-cache + ETag |
| API response publik yang stabil | public, max-age=60, s-maxage=600 |
| API response per-user | private, max-age=60 |
| Halaman admin, transaksi | no-store |
| Feed yang boleh stale sebentar | public, max-age=300, stale-while-revalidate=60 |
| Download user-generated | private, no-cache |
Contoh implementasi di Laravel:
return response()
->json($user)
->header("Cache-Control", "private, max-age=60")
->setEtag(md5($user->updated_at));
Laravel otomatis mengembalikan 304 kalau If-None-Match cocok.
Contoh di Node.js (Express):
app.get("/data.json", (req, res) => {
const data = loadData();
const etag = createEtag(JSON.stringify(data));
res.set("Cache-Control", "public, max-age=60");
res.set("ETag", etag);
if (req.headers["if-none-match"] === etag) {
return res.status(304).end();
}
res.json(data);
});
Debugging cache:
- Chrome DevTools → Network → check
Sizecolumn:(memory cache),(disk cache),(ServiceWorker)menunjukkan dari mana asalnya - Response header
Age: N— berapa detik response sudah di shared cache - Kalau perubahan tidak muncul: purge CDN, ubah nama file, atau turunkan TTL
Jebakan umum:
- Set
max-agepanjang untuk HTML → user terjebak versi lama, susah rollback - Lupa
Vary: Accept-Encoding→ user gzip dapat response plain, atau sebaliknya no-cachedisangka "jangan cache" — padahal bukan- ETag weak (
W/"abc") vs strong ("abc") — weak cuma cocok untuk validasi semantik, bukan byte-exact
Caching adalah salah satu optimasi paling kuat di web. Pakai yang tepat, website kamu jadi ringan sekaligus tetap update.