Kepala HTTP Cache-Control Apa Artinya no-cache, no-store, dan max-age Sebenarnya
Penjelasan praktis dari direktif Cache-Control — apa yang dilakukan oleh browser dan CDN terhadap no-cache, no-store, max-age, s-maxage, dan ETags. Termasuk kesalahan yang sering menyerang pengembang di produksi.
Anda mungkin telah menulis Cache-Control: no-cache dan mengasumsikan browser akan melewati cache secara utuh. Tidak akan terjadi. no-cache berarti "revalidasi sebelum disajikan dari cache." Jika Anda benar-benar ingin respons tidak disimpan sama sekali, itu adalah no-store. Kebingungan ini menyebabkan bug data usang di produksi yang sulit didiagnosis karena semuanya terlihat normal di tab Jaringan saat pertama kali dimuat.
Mari kita bahas setiap direktif secara jelas — apa yang dilakukan browser terhadapnya, apa yang dilakukan CDN terhadapnya, dan kesalahan yang muncul dari menggabungkannya.
Referensi Penuh Direktif Cache-Control
| Direktif | Perilaku browser | Perilaku CDN / proxy | Penggunaan umum |
|---|---|---|---|
max-age=N | Simpan selama N detik | Simpan selama N detik (kecuali s-maxage menggantinya) | Aset statis, respons API |
s-maxage=N | Dihapus | Simpan selama N detik | TTL CDN terpisah dari browser |
no-cache | Simpan tetapi revalidasi setiap permintaan | Simpan tetapi revalidasi setiap permintaan | Isi yang sering berubah dengan ETags |
no-store | Jangan simpan di mana pun | Jangan simpan di mana pun | Respons autentikasi, data pengguna sensitif |
must-revalidate | Tidak ada penyajian usang — revalidasi atau gagal | Tidak ada penyajian usang — revalidasi atau gagal | Respons API di mana data usang = rusak |
proxy-revalidate | Dihapus | Tidak ada penyajian usang pada cache bersama | Harus revalidasi khusus untuk CDN |
private | Browser mungkin menyimpan | Tidak boleh disimpan | Halaman berdasarkan pengguna |
public | Setiap cache mungkin menyimpan | Dapat disimpan | Sumber daya statis bersama |
immutable | Tidak revalidasi dalam maksimum usia | Bervariasi tergantung CDN | Aset yang di-hash / di-versi |
stale-while-revalidate=N | Sajikan usang selama N detik sambil mengambil versi baru | Bervariasi tergantung CDN | Kecepatan tanpa keusangan keras |
max-age dan s-maxage: TTL Browser vs CDN
max-age=N mengatakan kedua browser dan CDNs berapa detik respons tersebut segar. Setelah N detik, respons yang disimpan menjadi usang dan harus direvalidasi sebelum digunakan.
s-maxage=N adalah hanya untuk CDN. Browser mengabaikannya sepenuhnya. Jika Anda ingin CDN menyimpan selama satu jam tetapi browser hanya selama 5 menit:
Cache-Control: max-age=300, s-maxage=3600
Browser menyimpan selama 5 menit. CloudFront, Fastly, Nginx — mereka akan menggunakan 3600 detik. Penyebab umum: mengatur max-age=0 menganggapnya menonaktifkan penyimpanan. Tidak, itu tidak terjadi. max-age=0 berarti respons langsung usang — browser tetap menyimpannya dan direvalidasi, kemungkinan besar mendapatkan 304. Jika Anda tidak ingin disimpan sama sekali, gunakan no-store.
no-cache: Bukan Yang Anda Pikirkan
Cache-Control: no-cache tidak berarti "jangan gunakan cache." Ini berarti "jangan menyajikan dari cache tanpa direvalidasi terlebih dahulu dengan server."
Urutan saat browser memiliki respons yang disimpan dengan no-cache:
- Permintaan baru tiba untuk sumber daya yang disimpan
- Browser mengirimkan ETag respons yang disimpan (melalui
If-None-Match) atau timestamp Last-Modified ke server - Jika konten tidak berubah → server mengembalikan
304 Not Modifiedtanpa tubuh - Jika konten berubah → server mengembalikan
200 OKdengan respons baru
Keuntungan dibandingkan no-store: Anda tetap mendapatkan penghematan bandwidth dari respons 304. Jika konten Anda berubah secara teratur tetapi harus segar saat berubah, no-cache dikombinasikan dengan ETag adalah kombinasi yang tepat.
no-store: "Jangan Simpan Ini" yang Sebenarnya
Cache-Control: no-store berarti respons tidak boleh disimpan di mana pun — tidak di cache browser, tidak di CDN, tidak di proxy antar. Tidak ada salinan, berhenti.
Gunakan ini untuk:
- Respons autentikasi (token login, data sesi)
- Data pribadi sensitif
- Isi satu kali (konfirmasi pembayaran, halaman OTP)
Satu hal kecil: no-store tidak mencegah halaman muncul di cache kebalikan-kepulang (bfcache). Browser menyimpan snapshot di memori untuk kinerja navigasi yang terpisah dari cache HTTP. Jika Anda perlu menangani masalah tombol kembali setelah logout, hubungkan ke pageshow peristiwa dan periksa event.persisted.
harus-revalidasi: Tidak Ada Grace Usang
Spesifikasi caching HTTP memungkinkan cache menyajikan respons usang saat sumber utama tidak dapat diakses — fitur ketahanan yang sebagian besar pengembang tidak tahu. must-revalidate menghilangkan keleluasaan tersebut: setelah respons yang disimpan menjadi usang, cache harus direvalidasi atau mengembalikan 504. Tidak ada penyajian usang dalam kondisi apa pun.
# Without must-revalidate: CDN may serve stale if origin is slow or down
Cache-Control: max-age=3600
# With must-revalidate: stale = error, not a fallback
Cache-Control: max-age=3600, must-revalidate
Gunakan ini untuk respons API di mana penyajian data usang menghancurkan fungsi — jumlah inventaris, harga, status autentikasi — bukan hanya terlihat sedikit salah.
private vs public: Bug CDN yang Mengalirkan Data Pengguna
private berarti respons dimaksudkan untuk pengguna tertentu. Browser dapat menyimpannya; cache bersama (CDN, reverse proxy) harus tidak menyimpannya.
public secara eksplisit memungkinkan setiap cache — termasuk yang bersama — menyimpan respons tersebut. Beberapa cache hanya menyimpan respons permintaan yang autentik jika Anda secara eksplisit menandainya public.
Masalah dunia nyata yang ditimbulkan: pengembang menyalin Cache-Control: public, max-age=3600 dari aset statis ke halaman yang mencakup data pengguna. CDN menyimpan respons tersebut. Pengguna B membuat permintaan yang sama dan mendapatkan halaman pengguna A dari cache. Ini bukan hal teoritis — GitHub memiliki variasi dari hal ini pada tahun 2018. Tandai respons yang autentik atau berbasis pengguna secara eksplisit, bahkan jika Anda berpikir CDN "tahu" tidak perlu menyimpannya. private secara eksplisit, bahkan jika Anda berpikir CDN "tahu" tidak perlu menyimpannya.
ETags dan Permintaan Kondisional
ETags adalah cara server mengatakan "ini adalah jejak dari respons ini." Browser menyimpan ETag bersama dengan respons yang disimpan dan mengirimkannya kembali pada permintaan berikutnya melalui If-None-Match. Jika konten tidak berubah, server mengembalikan 304 Not Modified tanpa tubuh — sama dengan pengawasan keberadaan kehangatan seperti no-cache, jauh lebih sedikit bandwidth.
Itu no-cache + Alur ETag:
→ GET /api/config HTTP/1.1
← HTTP/1.1 200 OK
Cache-Control: no-cache
ETag: "abc123"
[full response body]
→ GET /api/config HTTP/1.1
If-None-Match: "abc123"
← HTTP/1.1 304 Not Modified
[no body — browser uses its cached copy]
Dua jenis ETag:
- ETag Kuat (
"abc123") — identik secara byte per byte. Diperlukan untuk dukungan permintaan range pada CDN. - ETag Lemah (
W/"abc123") — secara semantik setara tetapi tidak selalu identik secara byte. Cocok untuk revalidasi browser, tidak cocok untuk permintaan range.
Nginx menghasilkan ETag secara otomatis dari waktu modifikasi file dan ukuran. Express tidak menambahkannya secara default — gunakan app.set('etag', 'strong') atau etag middleware secara eksplisit.
Last-Modified dan If-Modified-Since
Konsep yang sama seperti ETag tetapi lebih kasar — berbasis waktu daripada berbasis hash konten. Server mencantumkan Last-Modified; browser mengirimkan If-Modified-Since pada permintaan berikutnya.
Masalahnya: jika Anda melakukan redeploy dan waktu modifikasi file berubah meskipun konten tidak berubah, cache akan diaktifkan secara tidak perlu. ETag berbasis hash konten tidak akan mengalami masalah ini. Gunakan ETag di mana mungkin dan anggap Last-Modified sebagai fallback untuk server yang tidak mendukung ETag.
Vary: Header yang Mengalikan Cache Secara Tidak Disadari
Itu Vary header mengatakan bahwa respons mungkin berbeda berdasarkan header permintaan lainnya. Setiap kombinasi unik dari header tersebut mendapatkan entri cache sendiri.
Vary: Accept-Encoding
Ini mengatakan kepada cache untuk menyimpan respons terpisah untuk gzip, brotli, dan encoding identitas. Benar dan umum. Yang berbahaya adalah: Vary: Cookie. Setiap pengguna memiliki set cookie unik, sehingga setiap pengguna mendapatkan entri cache sendiri — secara efektif mematikan cache bersama. Banyak kerangka kerja menambahkan Vary: Cookie secara diam-diam. Jika tingkat hit cache CDN Anda tiba-tiba rendah meskipun nilai max-age sangat besar, periksa header respons Anda untuk Vary: Cookie yang muncul dari middleware sesi.
Vary: * berarti "jangan simpan ini sama sekali" dalam praktiknya — setiap permintaan dianggap unik. Ini setara dengan no-store untuk CDN.
Membakar Cache dengan Parameter Query
Ketika Anda perlu memaksa unduhan versi terbaru dari aset yang di-versi, menambahkan parameter query adalah pendekatan standar — string query merupakan bagian dari URL, sehingga dianggap sebagai sumber daya baru oleh browser dan CDN:
/app.js?v=2.1.4
/styles.css?hash=a1b2c3d4e5f6
Jika Anda membangun parameter cache-busting secara dinamis dari hash konten atau string versi yang mungkin mengandung karakter khusus, pastikan untuk mengenkodkan persentase sebelum menambahkannya. Perangkat URL Encoder/Decoder mengatasi hal ini dengan cepat jika Anda sedang menguji atau membangun URL secara manual.
Tiga Kesalahan yang Menyerang Sebagian Besar Pengembang
1. Menggunakan no-cache ketika maksud Anda adalah no-store. Jika Anda menangani respons autentikasi, endpoint logout, atau hal lain yang berhubungan dengan PII, Anda ingin no-store. no-cache meninggalkan data di cache browser (hanya ditandai sebagai usang); no-store menghilangkan jejaknya sepenuhnya. Perbedaan ini penting saat pengguna berbagi perangkat.
2. Tidak mengatur s-maxage untuk pengendalian CDN. Linter dan Formatter YAML GitHub Actions s-maxage, CDN menggunakan max-age. Jika max-age singkat untuk kehangatan browser (misalnya 60 detik), CDN menyimpan selama 60 detik juga — yang kemungkinan besar bukan yang Anda inginkan. Pisahkan dua TTL secara eksplisit.
3. Menggunakan public pada endpoint yang mengembalikan data pengguna. Ini adalah insiden keamanan, bukan hanya bug performa. Setiap respons yang disesuaikan atau autentikasi harus private. Default ke private dan pilih public hanya untuk sumber daya yang benar-benar dibagikan.
Dari satu hex, kamu sekarang bisa menghasilkan palet lengkap, bernama secara semantik, dan siap untuk mode gelap dalam kurang dari 50 baris JavaScript. Langkah kunci:
Model mental: no-cache mengenai pengawasan keberadaan — respons berada di cache, tetapi perlu stamp persetujuan dari server sebelum digunakan. no-store mengenai tidak meninggalkan jejak. max-age adalah TTL browser Anda. s-maxage adalah TTL CDN Anda. ETag adalah yang membuat revalidasi murah.
Peroleh perbedaan private/public yang benar pada setiap endpoint yang menyentuh data pengguna. Kesalahan tunggal ini — menyalin header cache dari aset statis ke endpoint autentikasi — yang menjadi kebocoran data lintas pengguna ketika CDN mulai menyimpan respons yang disesuaikan.
Instal Ekstensi Kami
Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat
恵 Papan Skor Telah Tiba!
Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!
Alat Wajib Coba
Lihat semua Pendatang baru
Lihat semuaMemperbarui: Kita alat terbaru ditambahkan pada 15 Juni 2026
