HTTP Caching: Cache-Control, ETag, Validation — Web Fundamentals

Sebelumnya di "Caching Browser & CDN" kamu belajar gambaran besar. Di sini kita masuk ke detail header HTTP: directive Cache-Control yang sering membingungkan,

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:

Jebakan umum:

Caching adalah salah satu optimasi paling kuat di web. Pakai yang tepat, website kamu jadi ringan sekaligus tetap update.

Yang akan kamu pelajari