Cache-Control — header HTTP yang menentukan aturan caching sumber daya di sisi klien, server proxy, dan CDN menggunakan serangkaian direktif. Berbeda dengan header Expires yang sudah usang, Cache-Control mendukung puluhan kombinasi: max-age menetapkan waktu hidup dalam detik, private dan public mengelola ketersediaan cache, no-cache dan no-store — pemeriksaan paksa. Menurut Google Web Dev (2025), konfigurasi Cache-Control yang tepat dapat mengurangi waktu muat halaman sebesar 50-80% untuk kunjungan berulang. Ini menjadikan header tersebut penting untuk kinerja aplikasi web dan seluler.
Poin Utama
Cache-Control — header HTTP, distandarisasi dalam HTTP/1.1 (RFC 7234), yang memungkinkan server menentukan bagaimana dan berapa lama klien, proxy, dan CDN dapat menyimpan respons dalam cache. Berbeda dengan Expires (HTTP/1.0), Cache-Control menggunakan direktif — perintah teks yang digabungkan dengan koma: Cache-Control: public, max-age=3600, must-revalidate. Header ini memberikan kontrol yang presisi atas setiap mata rantai dalam rantai caching.
Caching adalah salah satu mekanisme fundamental kinerja web dan aplikasi seluler. Tanpanya, setiap permintaan pengguna akan langsung menuju server, menyebabkan beban berlebih dan keterlambatan. Cache-Control mendefinisikan tiga tingkat caching: browser/aplikasi (private cache), server proxy (shared cache), dan CDN (distributed cache). Setiap tingkat menafsirkan direktif dengan caranya sendiri.
Konfigurasi Cache-Control yang salah adalah salah satu penyebab paling umum masalah kinerja. Caching yang terlalu agresif menyebabkan pengguna melihat data usang. Caching yang terlalu lemah menyebabkan permintaan berlebihan ke server dan pemuatan lambat. Menurut Akamai (2025), optimalisasi Cache-Control untuk konten statis mengurangi beban server sebesar 70-90% dan meningkatkan waktu muat sebesar 40-60% untuk pengguna seluler.
Cache-Control muncul di HTTP/1.1 (RFC 2616, 1999) sebagai pengganti Expires. Expires memiliki masalah mendasar: menggunakan tanggal absolut yang bergantung pada zona waktu server dan klien. Cache-Control memecahkan masalah ini dengan beralih ke waktu relatif (max-age dalam detik sejak saat menerima respons). Kemudian dalam RFC 7234 (2014) ditambahkan direktif baru: immutable untuk statis, stale-while-revalidate dan stale-if-error untuk pemeriksaan tertunda.
Cache-Control mencakup lebih dari 10 direktif yang dibagi menjadi tiga kelompok: direktif permintaan (klien → server), direktif respons (server → klien), dan ekstensi. Dalam praktiknya, dalam pengembangan seluler digunakan 6-7 direktif respons dasar yang mencakup 95% skenario caching. Mari kita bahas masing-masing dengan contoh dan rekomendasi.
| Direktif | Arti | Contoh |
|---|---|---|
| max-age | Waktu hidup dalam detik sejak respons | max-age=3600 — 1 jam |
| s-maxage | max-age untuk shared cache (proxy, CDN) | s-maxage=86400 — 1 hari untuk CDN |
| public | Mengizinkan caching kepada semua orang (termasuk proxy) | public, max-age=3600 |
| private | Mengizinkan cache hanya untuk browser/aplikasi | private, max-age=600 |
| no-cache | Jangan gunakan tanpa pemeriksaan (304 wajib) | no-cache |
| no-store | Larang caching sepenuhnya | no-store |
| must-revalidate | Setelah max-age, wajib periksa ke origin | max-age=3600, must-revalidate |
| immutable | Sumber daya tidak akan berubah (untuk statis yang diberi versi) | max-age=31536000, immutable |
max-age — direktif terpenting. Ini melarang klien mengirim permintaan ke server untuk jangka waktu tertentu. Untuk statis (CSS, JS, gambar) max-age biasanya diatur dari 1 hari hingga 1 tahun. Untuk respons API — dari 0 detik (data selalu segar) hingga 5-10 menit (data referensi). s-maxage memungkinkan pengaturan waktu hidup yang berbeda untuk CDN dan browser: CDN menyimpan salinan 1 hari, browser — 1 jam.
Kedua direktif ini sering tertukar. no-cache tidak melarang caching — ia memerlukan pemeriksaan salinan cache pada setiap penggunaan melalui permintaan bersyarat (If-Modified-Since atau If-None-Match). Jika server merespons 304 — klien menggunakan cache. Jika 200 — memperbarui. no-store sebaliknya melarang penyimpanan respons di cache apa pun, termasuk disk dan memori. Gunakan no-store hanya untuk data sensitif — token, data pembayaran, dokumen pribadi.
Header Expires (HTTP/1.0) juga menunjukkan waktu hidup sumber daya, tetapi menggunakan tanggal absolut: Expires: Thu, 03 Jul 2026 12:00:00 GMT. Cache-Control max-age — waktu relatif dari saat respons. Perbedaan ini kritis untuk sistem terdistribusi: jika server dan klien berada di zona waktu berbeda, Expires dapat ditafsirkan secara salah. Cache-Control tidak memiliki masalah ini — 3600 detik selalu 3600 detik.
Ketika kedua header hadir, Cache-Control memiliki prioritas di atas Expires. Ini ditetapkan dalam RFC 7234: “Jika respons berisi Cache-Control dengan direktif max-age, penerima HARUS mengabaikan Expires”. Dalam praktiknya, disarankan untuk tidak mengembalikan Expires untuk klien modern, karena Cache-Control mencakup semua skenario Expires. Namun, untuk kompatibilitas mundur dengan proxy dan browser lama, kedua header dapat dikembalikan.
Expires sebagian besar bertahan untuk konten statis di Nginx dan Apache — server ini secara otomatis menambahkan kedua header. Jika dalam proyek Anda Anda menemukan Expires tanpa Cache-Control, gantilah dengan Cache-Control dengan max-age: presisi manajemen cache meningkat dan ketergantungan pada zona waktu dihilangkan. Untuk migrasi, cukup konfigurasi server untuk menambahkan Cache-Control sebagai ganti Expires.
# Nginx: Cache-Control untuk file statis
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable, max-age=2592000";
}
# Kebijakan berbeda untuk jenis konten yang berbeda
location /api/config {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
location /api/static-data {
expires 5m;
add_header Cache-Control "public, max-age=300";
}
Dalam konfigurasi Nginx untuk file statis (CSS, JS, gambar) Cache-Control diatur selama 30 hari dengan atribut immutable — atribut ini memberi tahu browser bahwa sumber daya tidak pernah berubah di bawah URL ini (penomoran versi melalui hash di nama file). Endpoint API menggunakan no-cache untuk data dinamis dan public dengan max-age pendek untuk data referensi — daftar yang sering diminta dan jarang berubah.
Dalam aplikasi seluler, Cache-Control memainkan peran khusus karena keterbatasan jaringan seluler: latensi tinggi, koneksi tidak stabil, batas lalu lintas. Caching yang tepat memungkinkan pengguna melihat data secara instan, bahkan offline, dan memperbaruinya di latar belakang. OkHttp di Android dan URLSession di iOS memiliki sistem caching bawaan yang memperhitungkan Cache-Control.
OkHttp menggunakan CacheInterceptor yang membaca Cache-Control dari respons dan secara otomatis mengelola caching. Jika server mengembalikan Cache-Control: max-age=3600, OkHttp tidak akan mengirim permintaan ke server selama satu jam. Setelah max-age kedaluwarsa, OkHttp mengirim permintaan bersyarat dengan If-Modified-Since dan If-None-Match. Konfigurasi cache di OkHttp: OkHttpClient.Builder().cache(Cache(directory, maxSize)).
fun createCachedClient(cacheDir: File): OkHttpClient {
return OkHttpClient.Builder()
.cache(Cache(cacheDir, 10L * 1024 * 1024))
.addNetworkInterceptor { chain ->
val response = chain.proceed(chain.request())
response.newBuilder()
.header("Cache-Control",
"public, max-age=300")
.removeHeader("Pragma")
.build()
}
.build()
}
Kode membuat OkHttpClient dengan cache 10 MB dan menggantikan Cache-Control melalui NetworkInterceptor. Jika server tidak mengembalikan Cache-Control atau menggunakan Expires, interceptor menambahkan public, max-age=300 (5 menit). Interceptor menghapus header Pragma usang (HTTP/1.0) untuk kompatibilitas. Dengan skema yang sama, caching di iOS bekerja melalui URLCache.shared dengan konfigurasi memoryCapacity dan diskCapacity.
Direktif stale-while-revalidate memungkinkan pengguna melihat cache usang (stale) sementara aplikasi memuat data segar di latar belakang. Ini memberikan efek respons instan: pengguna melihat konten segera, dan setelah satu detik konten diperbarui. Didukung oleh OkHttp mulai versi 3.10 dan URLCache di iOS 14+. Contoh: Cache-Control: max-age=3600, stale-while-revalidate=300 — 1 jam cache aktif, lalu 5 menit tampilan cache usang dengan pembaruan latar belakang.
Jenis sumber daya yang berbeda memerlukan strategi caching yang berbeda. Mari kita lihat konfigurasi optimal untuk skenario umum dalam pengembangan seluler. Untuk konten statis dengan hash di nama file (bundle.abc123.js) dapat mengatur max-age hingga 1 tahun dengan immutable. Untuk daftar API yang jarang diperbarui (katalog, kategori) — max-age dari 5 menit hingga 1 jam dengan stale-while-revalidate.
| Jenis sumber daya | Cache-Control | Penjelasan |
|---|---|---|
| Statis dengan versi | public, max-age=31536000, immutable | 1 tahun, file tidak berubah (hash di URL) |
| Statis tanpa versi | public, max-age=86400, must-revalidate | 1 hari dengan pemeriksaan wajib setelahnya |
| API: data referensi | public, max-age=600, stale-while-revalidate=60 | 10 menit cache + 1 menit stale |
| API: data pengguna | private, max-age=60 | 1 menit, hanya untuk pengguna tertentu |
| API: data sensitif | no-store | Larang caching sepenuhnya |
| Halaman HTML | no-cache, must-revalidate | Pemeriksaan setiap permintaan, 304 jika tidak berubah |
Penting untuk diingat tentang keamanan: untuk respons yang berisi data pribadi pengguna, selalu atur private. Tanpa direktif ini, proxy publik (misalnya perusahaan) dapat menyimpan respons dalam cache dan memberikannya kepada pengguna lain. Untuk token autentikasi dan informasi pembayaran, gunakan no-store — bahkan cache private tidak boleh menyimpan data ini ke disk.
Untuk memeriksa kebenaran Cache-Control, gunakan header Age (berapa detik cache disimpan) dan X-Cache (hit/miss di CDN). Di browser — tab Network, kolom Size menunjukkan “from disk cache” atau “304 Not Modified”. Jika sumber daya harus di-cache tetapi dimuat setiap kali — periksa apakah server tidak menambahkan Cache-Control: no-cache atau Pragma: no-cache bersama dengan direktif Anda.
Pertanyaan yang Sering Diajukan
max-age berlaku untuk semua cache (termasuk browser), s-maxage — hanya untuk shared cache (proxy, CDN). Jika s-maxage ditentukan, CDN mengabaikan max-age dan menggunakan s-maxage. Ini memungkinkan pengaturan waktu hidup yang berbeda untuk browser dan CDN.
Tidak, setelah mengirim respons dengan max-age, klien tidak akan mengirim permintaan sampai timer kedaluwarsa. Untuk membatalkan cache segera, Anda perlu mengubah URL sumber daya (menambahkan versi/hash) dan mengirim notifikasi push atau pesan WebSocket untuk reset paksa.
Direktif immutable (RFC 8246) memberi tahu browser bahwa sumber daya tidak akan pernah berubah di bawah URL ini. Browser bahkan tidak mencoba mengirim permintaan bersyarat saat menyegarkan halaman — menggunakan cache hingga max-age kedaluwarsa. Hanya berfungsi dengan file yang diberi versi.
Googlebot memperhitungkan Cache-Control: caching lama mempercepat pemindaian ulang. noindex dengan cache cepat — ok. no-store dapat memperlambat pengindeksan, karena Googlebot akan memuat halaman dari awal setiap kali. max-age yang terlalu pendek meningkatkan beban server saat pemindaian.
Melalui helmet atau middleware: res.set('Cache-Control', 'public, max-age=3600'). Untuk statis, gunakan express.static dengan parameter maxAge: express.static('public', {maxAge: '1y'}). Untuk rute dinamis — secara individual di setiap handler.
Kesimpulan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga