API caching artinya menyimpan salinan jawaban supaya request berikutnya gak perlu dikerjakan ulang oleh server, atau malah gak perlu dikirim sama sekali. Bayangin brosur harga dari toko buku. Di pojoknya tertulis "berlaku 1 menit". Selama masih berlaku, kamu baca brosurmu sendiri tanpa ke toko. Lewat dari itu, kamu telepon tokonya: "Brosur saya edisi v3, masih berlaku?" Kalau masih, toko cukup bilang "masih" tanpa mengirim brosur baru. Kalau sudah ganti, toko mengirim edisi terbaru.
🧪 Tebakan cepat: server menjawab
GET /books/1denganCache-Control: max-age=60. Di detik ke-10, admin mengubah harga Laskar Pelangi dari 89.000 jadi 79.000. Di detik ke-20, kamu membuka halaman buku itu lagi. Harga mana yang kamu lihat? Tebak dulu, jawabannya ada di bagian "Harga lama setelah update".
Cache-Control: berapa lama, dan siapa yang boleh menyimpan
Jawaban GET /books/1 dari API Toko Buku:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=60
ETag: "0c15462dedf3e7ea"
Last-Modified: Fri, 09 Oct 2026 08:00:00 GMT
{"id":1,"title":"Laskar Pelangi","author_id":1,"price":89000,"stock":12}
Cache-Control adalah header utama yang mengatur durasi cache di browser dan di CDN (server perantara yang menyimpan salinan untuk banyak user sekaligus). Isinya beberapa directive:
| Directive | Artinya |
|---|---|
max-age=60 |
salinan ini segar selama 60 detik; selama itu dipakai tanpa bertanya ke server |
public |
browser dan cache bersama (CDN) boleh menyimpan |
private |
cuma browser user itu yang boleh menyimpan; CDN gak boleh |
no-cache |
boleh disimpan, tapi wajib ditanyakan ke server dulu setiap kali dipakai |
no-store |
jangan disimpan sama sekali |
s-maxage=300 |
durasi khusus untuk cache bersama (CDN) |
stale-while-revalidate=300 |
sesudah kedaluwarsa, salinan lama boleh dipakai 300 detik lagi sambil mengambil yang baru di belakang |
Harga lama setelah update
| Detik | Kejadian | Harga yang kamu lihat |
|---|---|---|
| 0 | GET /books/1, server menjawab 200 dan browser menyimpan salinan |
89.000 |
| 10 | admin mengubah harga jadi 79.000 di server | |
| 20 | GET /books/1 lagi: salinan baru berumur 20 detik, masih segar, gak ada request |
89.000 |
| 65 | salinan sudah kedaluwarsa, browser bertanya ke server | 79.000 |
Kamu melihat 89.000, harga lama. Itu jawaban tebakannya. Selama max-age, browser gak bertanya sama sekali, jadi gak tahu datanya sudah berubah. Pilih max-age sepanjang waktu yang masih boleh basi. Data yang harus selalu terbaru pakai no-cache dan validator di bagian berikutnya.
ETag dan If-None-Match: 304 Not Modified
ETag adalah label versi sebuah jawaban, biasanya hash dari isi body-nya. Saat salinannya kedaluwarsa, browser bertanya dengan conditional request yang membawa ETag lamanya:
GET /books/1 HTTP/1.1
Host: api.tokobuku.test
If-None-Match: "0c15462dedf3e7ea"
- ETag masih sama: server menjawab
304 Not Modifiedtanpa body. Browser memakai salinannya lagi dan menghitung umurnya dari 0. Hemat bandwidth. - ETag berbeda (datanya berubah): server menjawab
200dengan body baru dan ETag baru.
Logika server-nya, dengan ETag sederhana "v" + version seperti di latihan:
function handleRequest(article, ifNoneMatch) {
const etag = "v" + article.version;
if (ifNoneMatch === etag) {
return { status: 304, body: null, etag };
}
return { status: 200, body: article, etag };
}
| Panggilan | etag |
Hasil |
|---|---|---|
handleRequest(article, null) dengan version: 3 |
v3 |
200, body artikel |
handleRequest(article, "v3") |
v3 |
304, body null |
handleRequest(article, "v2") |
v3 |
200, client punya versi lama |
"v" + article.versionmembuat ETag dari versinya, jadiversion: 9menghasilkanv9.ifNoneMatch === etagmembandingkan label milik client dengan label terbaru.304selalu tanpa body (null);200membawa artikelnya.
Di HTTP sungguhan, nilai ETag ditulis dengan tanda kutip (ETag: "v3") dan biasanya hash isi body. Lab memakai SHA-256 dari body lewat crypto.subtle.digest("SHA-256", ...), dan Express membuat ETag sendiri untuk res.json lalu menjawab 304 otomatis kalau If-None-Match cocok.
Last-Modified dan If-Modified-Since
Validator kedua memakai waktu. Server mengirim Last-Modified: Fri, 09 Oct 2026 08:00:00 GMT, dan browser bertanya dengan If-Modified-Since: Fri, 09 Oct 2026 08:00:00 GMT. Kalau datanya gak berubah sejak waktu itu, jawabannya 304. Ketelitiannya cuma 1 detik, jadi dua perubahan dalam detik yang sama gak terbedakan. Kalau request membawa keduanya, If-None-Match yang dipakai.
🤔 Cache bersama (CDN) dan stale-while-revalidate
- CDN menyimpan satu salinan untuk banyak user. User kedua yang meminta
GET /books/1dilayani CDN tanpa menyentuh server, dengan headerAgeyang menyebut umur salinannya. - Karena itu data milik satu user (profil, keranjang) harus
privateatauno-store. Kalaupublic, CDN bisa mengirim data user A ke user B. Cache-Control: max-age=60, stale-while-revalidate=300membuat halaman tetap instan: lewat 60 detik, salinan lama langsung dipakai sambil versi baru diambil di belakang. Library React bernama SWR memakai ide yang sama.
⚠️ Jebakan yang sering ketemu
- Mengira
no-cacheartinya jangan simpan.no-cacheboleh disimpan asal ditanyakan dulu. Yang melarang menyimpan ituno-store. publicuntuk data per user. Pakaiprivateuntuk jawaban yang berbeda per user.max-agepanjang untuk data yang sering berubah. User melihat harga lama sampai salinannya kedaluwarsa.- ETag yang gak ikut berubah. ETag dari
idsaja selalu sama, jadi server terus menjawab304walau datanya sudah berubah. Buat dari versi atau hash isinya.
TL;DR: Cache-Control mengatur durasi cache di browser dan CDN: max-age=N segar N detik, public boleh disimpan CDN, private cuma browser, no-cache simpan tapi tanya dulu, no-store jangan simpan. Selama max-age, browser gak bertanya, jadi update di server baru terlihat setelah kedaluwarsa. Setelah itu browser mengirim If-None-Match berisi ETag lamanya: cocok dijawab 304 Not Modified tanpa body, beda dijawab 200 dengan body dan ETag baru. Last-Modified dan If-Modified-Since melakukan hal yang sama dengan waktu. stale-while-revalidate memakai salinan lama sambil mengambil yang baru di belakang.