Caching HTTP: Lapisan yang Sering Kurang Dimanfaatkan
Setiap kali saya audit performa aplikasi web, satu hal yang hampir selalu saya temukan adalah header caching yang tidak diset sama sekali, atau diset asal-asalan. Padahal caching HTTP itu gratis: sudah dibakukan di protokol, didukung semua browser dan hampir semua reverse proxy, dan tidak butuh infrastruktur tambahan seperti Redis atau CDN mahal untuk mulai terasa manfaatnya. Masalahnya, caching sering dianggap topik "nanti saja kalau sudah lambat", padahal justru di situlah letak jebakannya: begitu aplikasi sudah besar, menambahkan strategi caching yang benar jadi lebih berisiko karena banyak bagian sistem sudah bergantung pada perilaku lama.
Di tutorial ini saya mau bahas dua mekanisme inti yang sering diabaikan: Cache-Control dan ETag, kenapa keduanya penting, dan kenapa banyak tim justru tidak memakainya.
Cache-Control: bukan sekadar on/off
Banyak developer mengira caching itu biner, boleh atau tidak boleh di-cache. Padahal header Cache-Control punya beberapa parameter yang saling melengkapi:
max-age: berapa lama response dianggap segar (fresh) tanpa perlu tanya ulang ke server.publicvsprivate: apakah response boleh disimpan oleh cache bersama (CDN, proxy) atau hanya oleh browser pengguna.no-store: jangan simpan sama sekali, cocok untuk data sensitif.no-cache: boleh disimpan, tapi wajib validasi ulang ke server sebelum dipakai — ini sering disalahpahami sebagai "jangan cache", padahal artinya "cache tapi validasi dulu".
Sebagai ilustrasi, untuk aset statis seperti file JS atau CSS yang sudah punya hash di nama filenya, saya biasa memakai kombinasi seperti ini:
Cache-Control: public, max-age=31536000, immutableSebaliknya, untuk endpoint API yang mengembalikan data yang sering berubah tapi masih layak di-cache sebentar (misalnya daftar produk), sesuatu seperti Cache-Control: private, max-age=60 sudah cukup mengurangi beban server tanpa membuat data terasa basi.
ETag: validasi tanpa kirim ulang seluruh body
Kalau Cache-Control mengatur berapa lama cache dianggap valid, ETag mengatur cara validasi setelah masa berlaku itu habis. Server menghasilkan semacam sidik jari (hash) dari isi response. Saat browser punya cache lama dan masa berlakunya habis, ia mengirim ulang request dengan header If-None-Match berisi ETag yang tersimpan. Kalau isi resource belum berubah, server cukup membalas 304 Not Modified tanpa body sama sekali — jauh lebih hemat bandwidth dibanding mengirim ulang seluruh payload.
Yang sering luput: ETag paling berguna justru untuk response yang tidak sering berubah tapi sulit ditentukan masa berlakunya di depan, misalnya representasi JSON dari sebuah record yang bisa diedit kapan saja. Di aplikasi Laravel, ini bisa dibangun manual dengan menghitung hash dari data lalu membandingkannya dengan header If-None-Match di request, atau memanfaatkan middleware yang sudah menyediakan mekanisme serupa.
Kenapa fitur ini sering tidak dipakai
Ada beberapa alasan umum yang saya lihat berulang. Pertama, banyak framework modern menonaktifkan caching secara default untuk endpoint dinamis demi menghindari bug data basi, dan defaultnya jarang direvisit. Kedua, tim khawatir salah konfigurasi bikin user melihat data lama — kekhawatiran yang valid, tapi solusinya bukan menghindari caching sama sekali, melainkan memilih strategi validasi yang tepat seperti ETag. Ketiga, caching HTTP tidak terlihat dari sisi kode aplikasi, sering luput dari review karena dianggap urusan infrastruktur, padahal biasanya cukup ditambahkan di layer controller atau middleware.
Takeaway
Caching HTTP bukan pengganti caching aplikasi seperti Redis, tapi lapisan pertama yang paling murah untuk diaktifkan. Mulai dari yang aman: Cache-Control agresif untuk aset statis yang punya versioning, dan ETag untuk response dinamis yang mahal dihitung tapi jarang berubah. Efeknya sering langsung terasa di latensi dan beban server, jauh sebelum harus menambah infrastruktur baru.