Caching HTTP untuk Website Pribadi — Menjaga Halaman Tetap Aktual dan Aset Tetap Cepat
Sebuah website pribadi bisa terasa cepat pada kunjungan kedua karena alasan sederhana: browser mungkin sudah menyimpan sebagian filenya. Namun, mekanisme yang sama juga dapat mempertahankan stylesheet lama, menampilkan HTML kemarin, atau menempatkan response personal di lokasi yang dapat digunakan ulang oleh shared cache. Karena itu, pertanyaan yang berguna bukan "Apakah website ini perlu memakai caching?" melainkan "Response mana yang boleh digunakan ulang, oleh siapa, dan selama berapa lama?"
HTTP sebenarnya sudah memiliki kosakata untuk menjawab pertanyaan tersebut. Kebijakan yang praktis tidak mengharuskan kita menghafal setiap directive. Namun, kita perlu membedakan freshness dari validation, memahami nama no-cache yang mudah disalahartikan, dan memperlakukan sebuah URL sebagai janji mengenai cara kontennya berubah.
Apa yang sebenarnya dilakukan HTTP cache?
RFC 9111 mendefinisikan cache sebagai penyimpanan lokal untuk response message beserta subsistem yang mengatur penyimpanan, pengambilan, dan penghapusannya. Menggunakan ulang response yang sesuai dapat mempersingkat waktu response dan menghindari pengiriman byte yang sama berulang kali. Origin juga tidak perlu memproses request lain yang setara.
Ada dua batas yang perlu diperhatikan. Private cache digunakan khusus oleh satu pengguna, biasanya di dalam browser. Shared cache dapat menggunakan ulang response untuk lebih dari satu pengguna; reverse proxy dan CDN adalah contoh yang umum. Response yang aman di browser seseorang belum tentu aman di shared cache.
Response tersimpan disebut fresh selama umurnya masih berada dalam freshness lifetime. Response yang fresh biasanya dapat digunakan ulang tanpa menghubungi origin. Setelah menjadi stale, response itu tidak serta-merta dibuang. Cache dapat bertanya kepada origin apakah salinannya masih valid. RFC 9111 menyebut proses ini validation atau revalidation.
Perbedaan ini penting karena caching memiliki dua cara berbeda untuk menghemat pekerjaan:
- Freshness dapat menghindari network request sepenuhnya selama jangka waktu tertentu.
- Validation tetap menghubungi server, tetapi dapat menghindari pengiriman ulang isi response ketika tidak ada perubahan.
Freshness dan validation adalah kontrol yang berbeda
Response directive max-age menetapkan freshness lifetime secara eksplisit dalam detik. Sebagai contoh, response berikut dapat tetap fresh selama satu jam:
Cache-Control: max-age=3600
Ini tidak menjamin bahwa setiap cache akan menyimpan response tersebut. HTTP caching bersifat opsional dan implementasi dapat menerapkan kebijakan penyimpanan serta eviction sendiri. Header ini menjelaskan kapan response yang tersimpan boleh digunakan ulang; ia bukan perintah untuk menyediakan ruang disk.
Validation memakai metadata yang mengidentifikasi sebuah representation. Origin dapat mengirim ETag, tanggal Last-Modified, atau keduanya. Kemudian, client dapat mengirim kembali metadata itu melalui If-None-Match atau If-Modified-Since. Jika representation yang dipilih belum berubah, server dapat menjawab dengan 304 Not Modified tanpa message content. Semantik validator dan conditional request secara terperinci dijelaskan dalam RFC 9110. Ketika kedua kondisi request yang relevan tersedia, If-None-Match didahulukan daripada If-Modified-Since.
Validator bukan freshness lifetime. Ia memberi cache pertanyaan yang lebih murah setelah sebuah response perlu diperiksa. Sebaliknya, max-age yang panjang dapat menunda pertanyaan itu sampai response menjadi stale. Kebijakan yang baik sering memakai kedua mekanisme tersebut, tetapi untuk tugas yang berbeda.
Directive dengan nama yang paling mudah membingungkan
Cache-Control: no-cache bukan berarti "jangan simpan." Artinya, response yang tersimpan tidak boleh digunakan ulang tanpa validation yang berhasil. Ini berguna untuk URL halaman yang stabil tetapi kontennya dapat berubah: cache boleh menyimpan salinan, sementara origin tetap mendapat kesempatan untuk mengonfirmasinya sebelum digunakan ulang.
Cache-Control: no-cache
ETag: "page-revision-42"
Cache-Control: no-store adalah directive yang meminta cache agar tidak menyimpan response. Directive ini sesuai ketika penyimpanan response itu sendiri tidak dapat diterima, walaupun memasangnya di semua tempat mengorbankan perilaku browser dan penghematan transfer yang bermanfaat. Directive ini juga tidak bekerja mundur untuk menghapus response lama yang sudah tersimpan pada URL tersebut. Perbedaan praktis dan tradeoff-nya dijelaskan dengan baik dalam panduan HTTP caching MDN.
private menjawab pertanyaan yang berbeda. Directive ini mencegah shared cache menyimpan response lengkap, tetapi tetap mengizinkan penyimpanan di private cache. Karena itu, halaman akun personal dapat memakai:
Cache-Control: private, no-cache
Ini adalah titik awal yang masuk akal, bukan jawaban universal. Response yang memuat informasi sangat sensitif mungkin membutuhkan no-store. Artikel publik juga tidak otomatis menjadi private hanya karena pengunjung memiliki cookie. Kebijakan perlu mengikuti isi response dan batas privasi yang sebenarnya, bukan sekadar kekhawatiran samar terhadap caching.
Kebijakan kecil berdasarkan tiga jenis resource
Caching lebih mudah dipahami ketika resource dikelompokkan berdasarkan cara perubahannya. Untuk CMS kecil atau website pribadi, tiga kelompok sudah mencakup sebagian besar permukaan.
1. HTML publik dengan URL stabil
URL artikel seharusnya tetap mengidentifikasi artikel tersebut setelah diedit. Memberikan freshness lifetime yang sangat panjang pada HTML berarti origin tidak dapat memperbaiki salinan cache sampai masa itu berakhir, kecuali managed cache menyediakan mekanisme purge terpisah. Kebijakan yang konservatif adalah:
Cache-Control: no-cache
ETag: "article-252-revision-3"
Last-Modified: Sat, 22 Aug 2026 17:16:09 GMT
Nilai di atas hanya ilustrasi, bukan header yang diamati dari website ini. Dalam CMS nyata, validator harus berubah ketika representation yang dipilih berubah. Membuat ETag dari revisi konten yang dapat dipercaya bisa lebih ringan daripada menghitung hash seluruh response hasil render pada setiap request, tetapi implementasi yang tepat bergantung pada template, lokalisasi, compression, dan pilihan representation lainnya.
2. Static asset dengan versi
Stylesheet bernama app.css dapat berubah dengan URL yang tetap sama. Sebaliknya, file bernama app.a81c9e.css dapat memakai URL baru setiap kali kontennya berubah. Karena cache key setidaknya mencakup request method dan target URI, nama file baru menghasilkan lookup baru alih-alih bersaing dengan byte lama.
Cache-Control: public, max-age=2592000, immutable
Tiga puluh hari di sini hanyalah contoh, bukan angka ajaib. Syarat yang lebih penting adalah: URL bertanda immutable tidak boleh menyajikan konten berbeda di kemudian hari. Jika proses deployment menimpa file di lokasi yang sama, hapus immutable dan pilih lifetime yang lebih pendek. Long-lived caching bekerja karena URL berversi membuat perubahan menjadi eksplisit, bukan karena ada satu durasi yang optimal untuk semua keadaan.
3. Response personal atau sensitif
Halaman yang berbeda untuk setiap pengguna terautentikasi tidak boleh tersedia untuk penggunaan ulang umum oleh shared cache. private, no-cache dapat mempertahankan validation di private browser. no-store dapat menjadi pilihan yang lebih ketat ketika penyimpanan itu sendiri menjadi masalah. Authentication juga memiliki aturan shared cache khusus dalam RFC 9111. Karena itu, membuat konten terautentikasi dapat di-cache secara publik harus menjadi keputusan yang disengaja dengan cache key yang jelas, bukan akibat header static asset yang diwariskan tanpa sengaja.
Vary adalah bagian dari ketepatan, bukan hiasan
Sebuah URL tidak selalu cukup untuk memilih representation. URL yang sama dapat mengembalikan konten gzip atau Brotli berdasarkan Accept-Encoding, atau konten dalam bahasa berbeda berdasarkan Accept-Language. Response field Vary memberi tahu cache request field mana yang harus cocok sebelum response tersimpan dapat digunakan ulang.
Vary: Accept-Encoding
Tanpa Vary yang sesuai, cache dapat memilih representation yang salah. Namun, memakai Vary pada field dengan banyak kemungkinan nilai dapat memecah cache dan mengurangi reuse. MDN secara khusus mengingatkan agar tidak sembarangan memakai Vary pada User-Agent; feature detection atau URL yang stabil sering kali lebih bersih. Website multibahasa juga dapat menghindari language negotiation sepenuhnya dengan memberikan URL tersendiri untuk setiap terjemahan, sambil tetap memakai Vary pada encoding ketika server mengompresi response secara dinamis.
Titik awal NGINX yang konservatif
NGINX dapat menambahkan response field melalui add_header. Contoh berikut mengasumsikan file di bawah /static/ memiliki nama berversi dan HTML publik ditangani oleh catch-all location:
location /static/ {
add_header Cache-Control "public, max-age=2592000, immutable" always;
}
location / {
add_header Cache-Control "no-cache" always;
try_files $uri $uri/ /index.php?$query_string;
}
Ini bukan janji konfigurasi siap pakai. Dokumentasi resmi NGINX headers module menjelaskan bahwa directive add_header hanya diwariskan dari level konfigurasi sebelumnya ketika level saat ini tidak mendefinisikan add_header sendiri. Parameter always juga mengubah batasan status code default. Karena itu, nested location, PHP handler, atau field Cache-Control yang dibuat aplikasi dapat mengubah atau menggandakan hasilnya.
Pilih satu layer yang menjadi otoritas untuk setiap kebijakan, periksa konfigurasi NGINX lengkap yang sudah dirender, lalu uji route sebenarnya. Secara khusus, jangan terapkan kebijakan static asset pada upload pengguna atau URL asset yang dapat ditimpa tanpa perubahan nama.
Verifikasi perilaku, jangan hanya percaya pada file konfigurasi
Header dalam file konfigurasi baru sebatas niat. Periksa response origin untuk sampel HTML, asset, error page, dan route terautentikasi:
curl -sSI https://example.com/article/
curl -sSI https://example.com/static/app.a81c9e.css
Cari Cache-Control, ETag, Last-Modified, Vary, dan, ketika ada shared cache, Age. Setelah itu, ambil validator sebenarnya dari response pertama dan buat conditional request:
curl -sSI \
-H 'If-None-Match: "validator-from-the-response"' \
https://example.com/article/
Response 304 hanya diharapkan ketika validator masih cocok dan route tersebut menerapkan kondisi itu. Jika konten berubah, full response adalah hasil yang benar. Lakukan pengujian juga setelah deployment: pastikan HTML merujuk ke URL asset berversi yang baru dan URL versi lama masih mengembalikan byte aslinya selama client mungkin menyimpannya.
Hal yang tidak diselesaikan kebijakan ini
HTTP header tidak dapat melakukan purge secara aktif pada setiap browser atau intermediary yang sudah memiliki response fresh. Managed CDN mungkin menyediakan kontrol purge, tetapi itu adalah operasi khusus produk di luar model caching standar. Service worker menambahkan programmable cache lain dengan lifecycle tersendiri. Keduanya sebaiknya tidak dicampurkan ke kebijakan awal tanpa pengujian terpisah.
Caching juga tidak memperbaiki proses deployment yang tidak stabil. Lifetime panjang memperbesar dampak kesalahan ketika URL yang seharusnya immutable justru ditimpa. Validator menjadi tidak dapat diandalkan ketika tidak memperhitungkan setiap perubahan representation. Field Vary yang terlalu luas dapat membuang ruang penyimpanan, sedangkan yang kurang dapat mengembalikan konten yang salah. Semua ini adalah alasan untuk melakukan rollout berdasarkan jenis resource dan mengamati perilakunya, bukan alasan untuk menonaktifkan caching di semua tempat.
Kesimpulan
Kebijakan caching yang sehat dimulai dari model perubahan resource dan batas privasinya. HTML publik dengan URL stabil dapat disimpan tetapi perlu divalidasi ulang. Asset yang benar-benar berversi dapat tetap fresh lebih lama. Response personal memerlukan batas private, sedangkan response sensitif mungkin tidak boleh disimpan sama sekali. Validator menghemat transfer konten; freshness dapat menghemat request itu sendiri; Vary menjaga reuse tetap terhubung dengan representation yang tepat.
Pendekatan yang hati-hati bukanlah mencari max-age sepanjang mungkin. Yang lebih penting adalah menjadikan setiap URL sebagai janji yang jujur, menetapkan aturan secara eksplisit, dan memverifikasi response yang benar-benar diterima pengguna serta intermediary.
References
- RFC 9111: HTTP Caching — RFC Editor / IETF, June 2022.
- RFC 9110: HTTP Semantics — RFC Editor / IETF, June 2022.
- HTTP caching — MDN Web Docs, last modified August 14, 2026.
- Module ngx_http_headers_module — NGINX official documentation.
