Sajikan gambar melalui CDN
Varian gambar, pengubahan ukuran, key cache, invalidasi, dan cara menjaga gambar privat tetap privat di balik CDN.
Pertanyaan
Gimana menyajikan satu gambar ke ribuan perangkat dengan ukuran berbeda tanpa membanjiri origin, dan gimana mengganti atau mengunci gambar itu dengan benar?
Setelah baca ini, kamu bisa merancang varian gambar dan key cache-nya, memilih antara invalidasi dan URL berversi, dan menjaga konten privat nggak masuk ke cache bersama.
Prasyarat dan contoh
Kamu sudah paham cache dan TTL (M06) dan alur unggah ke storage (M15.01). Gambar asli sudah berstatus siap di object storage.
Toko online menyimpan foto produk 4000x3000 piksel, rata-rata 3,5 MB. Halaman daftar menampilkan 40 foto di layar yang butuh thumbnail 300 px lebar. Kalau tiap perangkat mengunduh aslinya, satu halaman membawa 40 x 3,5 MB = 140 MB. Thumbnail WebP 300 px kira-kira 15 KB, jadi 40 foto hanya 600 KB. Selisihnya lebih dari 200 kali lipat.
Mekanisme
Varian, bukan satu gambar. Simpan asli apa adanya, lalu hasilkan varian: lebar (300, 600, 1200), format (WebP atau AVIF dengan cadangan JPEG), dan kualitas. Tiap kombinasi punya URL berbeda. Jumlahnya cepat membesar: 3 lebar x 2 format = 6 varian per foto, dan 1 juta foto berarti 6 juta objek kalau semuanya dibuat di awal.
Dua strategi pembuatan.
- Eager: varian dibuat saat gambar diunggah, lewat job (M15.03 memakai pola yang sama). Penyajian sederhana dan cepat, tapi menghasilkan varian yang mungkin nggak pernah diminta.
- On-demand: varian dibuat waktu pertama diminta, disimpan, dan disajikan dari cache. Penyimpanan hemat, tapi request pertama lambat. Tanpa batas, penyerang bisa meminta
w=1,w=2, ...w=8000dan memaksa origin menghitung ribuan varian. Batasi parameter ke daftar yang diizinkan (whitelist), bukan angka bebas.
Key cache. CDN menyimpan respons berdasarkan key, biasanya URL lengkap plus header tertentu. Hal yang menentukan isi respons harus masuk key, hal yang nggak boleh jangan:
bagus: /img/p/8841/orig-v3.jpg?w=300&fmt=webp
buruk: /img/p/8841.jpg + Cookie sesi (key beda per pengguna, hit rate jatuh)
risiko: pilih format dari header Accept tanpa Vary (WebP tersaji ke klien yang nggak bisa baca)
Kalau format dipilih lewat negosiasi Accept, respons harus membawa Vary: Accept atau key cache harus memasukkannya. Kalau enggak, satu klien bisa meracuni cache buat klien lain.
Invalidasi vs URL berversi. Ada dua cara menggantikan gambar. Purge menghapus entri di banyak lokasi CDN: bisa memakan detik sampai menit, dan kadang berbiaya per permintaan. URL berversi (orig-v3.jpg, atau hash isi di nama berkas) mengubah key sehingga gambar lama dan baru hidup berdampingan. Untuk gambar yang jarang berubah, pakai URL berversi dengan Cache-Control: public, max-age=31536000, immutable. Aplikasi cuma perlu menulis URL baru ke metadata. Purge disisakan untuk keadaan darurat, misalnya gambar yang harus segera hilang.
Konten privat. Gambar yang hanya boleh dilihat pemiliknya (misalnya tanda pengenal) nggak boleh masuk cache publik dengan key yang bisa ditebak. Pilihannya: signed URL berumur pendek yang ditandatangani oleh aplikasi dan diverifikasi CDN, atau Cache-Control: private dan melewati cache bersama. Dengan signed URL, parameter tanda tangan harus dikeluarkan dari key cache (kalau tidak, tiap tanda tangan baru adalah cache miss), tapi verifikasi tetap dijalankan sebelum cache dilayani.
Saat rancangan ini gagal
- Cache miss serentak. Gambar baru viral, ribuan klien meminta varian yang belum ada, dan origin menghitung varian yang sama berkali-kali. Gabungkan request ke key yang sama (request coalescing) atau buat varian utama lebih dulu.
- Parameter bebas.
?w=yang menerima angka apa saja menghasilkan jutaan objek unik dan tagihan origin. Whitelist ukuran. - Purge yang nggak sampai. Gambar diganti di URL yang sama, purge hanya menjangkau sebagian lokasi, dan sebagian pengguna melihat gambar lama selama TTL. Karena itu URL berversi lebih bisa diandalkan.
- Privat yang bocor. Respons berisi gambar pengguna A dicache dengan key tanpa identitas, lalu disajikan ke pengguna B. Periksa header cache dan key untuk setiap jalur privat.
- Gambar asli ikut tersaji. Varian diproses, tapi URL asli 4000 px tetap publik dan dipakai langsung oleh halaman. Sajikan hanya varian ke klien.
Latihan
Marketplace punya 2 juta foto produk (asli rata-rata 3 MB) dan 5 juta tampilan halaman per hari, masing-masing 20 gambar. Penjual kadang mengganti foto. Foto verifikasi identitas penjual juga disimpan di storage yang sama. Rancang varian, key cache, cara mengganti foto, dan perlakuan foto verifikasi.
Lihat pembahasan
Varian: lebar 200, 400, 800 dalam WebP dengan cadangan JPEG, dibuat eager untuk 400 (dipakai halaman daftar) dan on-demand untuk yang lain. Parameter hanya menerima tiga lebar tadi, selain itu 400 Bad Request. Asli nggak pernah disajikan ke publik.
Beban: 5 juta x 20 = 100 juta gambar per hari. Dengan 400 px sekitar 30 KB, itu 3 TB per hari lewat CDN. Kalau hit rate 95%, origin cuma melayani 5 juta request per hari (sekitar 58 per detik rata-rata), dan itu terkelola. Tanpa varian, 100 juta x 3 MB = 300 TB per hari.
Key: path + lebar + format, tanpa cookie. Kalau format dipilih lewat Accept, masukkan ke key atau Vary: Accept.
Mengganti foto: URL berversi berbasis hash isi, misalnya /img/p/8841/9f3a1c/400.webp, dengan max-age=31536000, immutable. Penjual mengganti foto, aplikasi menulis hash baru ke metadata produk, dan halaman otomatis memakai URL baru. Nggak perlu purge. Purge hanya untuk foto yang harus ditarik segera (pelanggaran kebijakan).
Foto verifikasi: bucket dan prefix terpisah, nggak lewat distribusi publik. Disajikan lewat signed URL berumur 5 menit setelah cek izin staf, dengan Cache-Control: private, no-store.
Terkait
- Sebelumnya: Unggah langsung ke penyimpanan (M15.01) menjelaskan bagaimana gambar asli sampai di storage dan berstatus siap.
- Selanjutnya: Proses video secara asinkron (M15.03) memakai pola job yang sama buat transformasi yang lebih berat.
- Caching di M06 membahas TTL, stampede, dan penggabungan request yang dipakai di sini.
- Lab L15 belum tersedia.
Catatan
- Asumsi: CDN dengan kontrol key cache, header
Vary, dan verifikasi signed URL di tepi. Fitur sebenarnya berbeda antarpenyedia. - Ukuran file contoh (15 KB, 30 KB) perkiraan buat ilustrasi, bukan hasil benchmark. Ukur dengan gambar sendiri.
- Yang nggak dibahas: pemilihan encoder, responsive
srcsetsecara detail, dan watermarking. - Terakhir ditinjau: 26 Oktober 2026.