Cache API aman di edge bukan sekadar menambahkan Cache-Control lalu berharap latensi turun. Begitu respons disimpan di edge, Anda harus memastikan bahwa data publik memang aman dibagikan, data privat tidak bocor antar pengguna, dan request yang diulang oleh client, proxy, atau sistem webhook tetap idempoten.
Masalah utamanya hampir selalu sama: cache key terlalu umum, Authorization diabaikan, cookie membuat respons personal ikut tercache, atau retry webhook menulis data dua kali karena lapisan cache dan origin tidak punya kontrak yang jelas. Solusinya adalah mendesain API dengan aturan eksplisit tentang apa yang boleh di-cache, untuk siapa, dan dengan kunci apa.
Kapan respons API boleh di-cache di edge?
Aturan pertama: jangan mulai dari “semua bisa di-cache”. Mulailah dari klasifikasi respons.
1. Data publik
Respons publik adalah data yang sama untuk semua pengguna, misalnya:
- daftar produk publik,
- halaman katalog,
- metadata konten publik,
- konfigurasi frontend non-rahasia.
Jenis ini biasanya aman disimpan di edge dengan public cache policy. Anda masih perlu mengontrol masa berlaku dan validasi ulang dengan ETag.
2. Data privat per pengguna
Respons privat adalah data yang tergantung identitas atau hak akses, misalnya:
- profil pengguna,
- riwayat transaksi,
- dashboard internal,
- fitur yang hasilnya berbeda berdasarkan role atau tenant.
Secara default, respons ini jangan di-cache bersama di edge kecuali Anda benar-benar membuat cache key yang terisolasi per pengguna atau per identitas akses. Dalam banyak kasus, lebih aman memakai Cache-Control: private atau tidak meng-cache sama sekali di shared cache.
3. Respons campuran
Kasus paling berbahaya adalah respons yang terlihat publik tetapi diam-diam personal, misalnya endpoint /articles yang menambahkan flag isBookmarked untuk pengguna login. Jika respons seperti ini di-cache sebagai publik, pengguna lain bisa menerima versi yang salah, atau lebih buruk, data personal ikut terbawa.
Jika satu endpoint memiliki perilaku berbeda untuk pengguna anonim dan pengguna terautentikasi, anggap endpoint tersebut berisiko tinggi sampai kontrak cache-nya benar-benar eksplisit.
Kontrak header yang perlu benar: Cache-Control, ETag, Vary, dan stale-while-revalidate
Cache-Control untuk data publik vs privat
Header Cache-Control menentukan apakah shared cache seperti edge boleh menyimpan respons.
Contoh aman untuk data publik:
Cache-Control: public, max-age=60, stale-while-revalidate=300
ETag: "articles-v42"Artinya:
public: boleh disimpan di shared cache,max-age=60: selama 60 detik respons dianggap segar,stale-while-revalidate=300: edge boleh menyajikan versi lama sambil menyegarkan ke origin,ETag: origin dan edge bisa melakukan validasi ulang hemat bandwidth.
Contoh aman untuk data privat:
Cache-Control: private, no-cache
ETag: "user-123-profile-v8"Di sini private memberi sinyal bahwa respons ditujukan untuk satu pengguna, bukan untuk shared cache. no-cache bukan berarti tidak boleh disimpan sama sekali; artinya respons harus divalidasi ulang sebelum dipakai kembali. Untuk data sensitif, banyak tim memilih no-store jika ingin memastikan tidak ada penyimpanan di lapisan perantara.
Peran ETag: validasi ulang tanpa menebak-nebak
ETag adalah pengenal versi representasi resource. Saat client atau edge punya salinan lama, ia bisa mengirim:
If-None-Match: "articles-v42"Jika konten belum berubah, origin cukup menjawab 304 Not Modified tanpa body. Ini penting untuk edge caching karena:
- mengurangi transfer data,
- menghindari regenerasi payload besar,
- membuat revalidasi lebih aman dibanding TTL panjang tanpa validasi.
Jangan membuat ETag yang sama untuk representasi yang berbeda. Jika respons dipengaruhi locale, tenant, atau bentuk field berdasarkan permission, maka ETag juga harus merepresentasikan variasi itu atau Anda harus memisahkannya lewat cache key dan Vary.
Vary: memberi tahu bahwa satu URL punya beberapa representasi
Vary penting ketika respons berubah berdasarkan header request tertentu. Contoh umum:
Vary: Accept-Encoding, Accept-LanguageUntuk API, Vary bisa relevan jika:
- bahasa memengaruhi hasil,
- format respons bergantung pada header tertentu,
- perilaku anonim vs login dibedakan oleh header yang memang ingin dijadikan bagian dari cache key.
Namun ada jebakan besar: menambahkan Vary: Authorization tidak otomatis berarti semua edge cache akan aman dan efisien. Secara teori ini memberi tahu bahwa respons bervariasi berdasarkan header Authorization, tetapi pada praktiknya:
- banyak token unik per request atau per sesi, sehingga hit ratio hampir nol,
- beberapa platform cache tidak menangani shared caching atas respons ber-
Authorizationseperti yang Anda bayangkan, - jika token membawa konteks sensitif, menjadikannya bagian dari cache key bisa meningkatkan kompleksitas dan risiko observability/logging.
Kesimpulannya: jangan mengandalkan Vary: Authorization sebagai solusi utama untuk data privat. Lebih baik pisahkan endpoint publik dan privat, atau gunakan cache key yang diturunkan dari identitas stabil dan aman bila Anda benar-benar memerlukan edge caching untuk data privat.
stale-while-revalidate: cepat, tapi tetap butuh batasan
stale-while-revalidate berguna untuk mengurangi latensi puncak dan mencegah herd effect saat cache kedaluwarsa. Tetapi ada dua aturan:
- Gunakan terutama untuk data publik atau data yang aman disajikan sedikit lama.
- Jangan memakainya untuk respons yang sensitif terhadap perubahan otorisasi, status pembayaran, atau entitlement pengguna, kecuali Anda menerima risiko data sedikit basi.
Contoh buruk: status langganan premium di-cache 5 menit dengan stale-while-revalidate. Pengguna yang baru dicabut aksesnya bisa tetap melihat versi lama untuk sementara.
Jebakan umum pada Authorization, cookie, dan cache key
Anti-pattern: URL sama, respons berbeda, cache key sama
Misalnya backend punya endpoint:
GET /me/ordersLalu edge cache memakai key berbasis URL saja. Ini langsung berbahaya karena pengguna A dan B mengakses URL yang sama tetapi seharusnya menerima data berbeda.
Jika endpoint bersifat personal, pilihan aman adalah:
- tidak menyimpannya di shared cache, atau
- membangun cache key yang mengandung identitas pengguna/tenant yang sudah dinormalisasi dan aman.
Authorization header sering membuat asumsi salah
Banyak tim mengira “kalau ada Authorization, cache pasti tidak akan menyimpan”. Asumsi ini tidak universal. Perilaku tergantung platform, konfigurasi, dan kode edge Anda sendiri. Jika Anda menulis logika cache manual di edge, Anda bisa saja tanpa sengaja menyimpan respons privat walaupun request membawa bearer token.
Prinsip yang aman:
- Jika request membawa
Authorization, anggap respons tidak boleh masuk shared cache kecuali ada desain eksplisit sebaliknya. - Jika sebagian data tetap publik walau user login, pertimbangkan endpoint terpisah untuk versi publik.
- Hindari membuat cache key langsung dari token mentah.
Cookie-based auth lebih mudah bocor daripada yang terlihat
Autentikasi berbasis cookie sering lebih sulit dideteksi di layer edge, terutama jika endpoint publik dan privat berbagi domain/path yang sama. Risiko umumnya:
- cookie sesi ikut terkirim ke endpoint yang tampak publik,
- backend menambah field personal karena mendeteksi cookie,
- edge menyimpan respons yang ternyata sudah terpersonalisasi.
Mitigasi praktis:
- untuk endpoint yang ingin benar-benar publik, pastikan origin mengabaikan cookie autentikasi,
- gunakan path atau host terpisah untuk API publik dan privat bila memungkinkan,
- jangan menambahkan personalisasi implisit pada endpoint yang direncanakan untuk shared cache.
Cache key harus berbasis identitas representasi, bukan mentah dari request
Cache key yang baik merepresentasikan hal-hal yang benar-benar mengubah body respons. Biasanya ini mencakup:
- metode request,
- path,
- query yang relevan,
- locale atau format respons,
- tenant atau identitas stabil jika memang meng-cache data privat secara terisolasi.
Jangan memasukkan semua header secara membabi buta. Jangan juga memakai token JWT mentah sebagai key karena:
- ukuran key membengkak,
- hit ratio rendah,
- token bisa berganti walau identitas sama,
- risiko token terekspos lewat log atau tooling.
Jika harus meng-cache respons per pengguna di edge, lebih aman turunkan key dari identitas stabil seperti tenant_id:user_id:resource_version, bukan dari bearer token mentah.
Skenario backend yang aman dan yang berbahaya
Skenario aman: katalog publik
Endpoint:
GET /v1/products?category=books&lang=idKarakteristik:
- sama untuk semua pengguna,
- berubah tidak terlalu sering,
- bahasa memengaruhi representasi.
Kontrak yang masuk akal:
Cache-Control: public, max-age=120, stale-while-revalidate=600
ETag: "products-books-id-v154"
Vary: Accept-LanguageAtau jika bahasa ditentukan eksplisit lewat query, Anda mungkin tidak butuh Vary: Accept-Language karena query sudah menjadi bagian dari key.
Skenario berbahaya: endpoint publik yang diam-diam personal
Endpoint:
GET /v1/products/123Untuk pengguna anonim, respons hanya berisi detail produk. Untuk pengguna login, origin menambah:
is_in_wishlist,last_viewed_price,- promo spesifik akun.
Jika edge menyimpan salah satu versi itu sebagai respons publik, hasilnya salah atau bocor. Solusi yang lebih aman:
- pisahkan endpoint publik dan state personal, misalnya
/v1/products/123dan/v1/me/product-flags/123, atau - jangan cache respons gabungan di shared cache.
Skenario aman terbatas: caching privat terisolasi per tenant
Ada kasus internal B2B di mana dashboard dibaca sangat sering dan data per tenant cukup stabil. Secara teori edge caching tetap bisa dipakai jika:
- cache key memuat tenant yang tervalidasi,
- respons tidak bercampur antar tenant,
- TTL pendek,
- invalidation jelas,
- audit logging memadai.
Tetap saja, kompleksitasnya jauh lebih tinggi dibanding caching data publik. Banyak sistem lebih aman menggunakan cache di origin atau aplikasi daripada shared edge cache untuk data seperti ini.
Idempotensi saat webhook, retry, dan cache terlibat
Edge cache umumnya relevan untuk request GET dan kadang HEAD. Tetapi dalam sistem nyata, lapisan cache bisa tetap memengaruhi alur retry, validasi status, atau pembacaan setelah penulisan. Karena itu idempotensi tetap harus dirancang di tingkat API dan backend.
Kenapa webhook dan retry sering rusak
Masalah umum:
- provider webhook mengirim event yang sama lebih dari sekali,
- client mengulang request karena timeout,
- load balancer atau worker menjalankan retry internal,
- response sukses lambat sampai ke client, sehingga client mengira gagal lalu mengirim ulang.
Jika endpoint write seperti POST /payments/webhook tidak idempoten, satu event bisa membuat dua insert, dua email, atau dua perubahan status.
Aturan penting: jangan cache endpoint write
Untuk endpoint webhook, callback, atau mutasi data:
Cache-Control: no-storeLebih penting lagi, logika handler harus idempoten secara bisnis. Cache tidak boleh menjadi mekanisme pencegah duplikasi write.
Pola idempotency key
Untuk endpoint yang bisa di-retry client, gunakan idempotency key yang stabil per operasi. Backend menyimpan hasil pemrosesan berdasarkan key tersebut.
POST /v1/orders
Idempotency-Key: 8b6c2d3e-...Pseudocode handler:
if idempotency_key exists and already_processed:
return previous_response
begin transaction
check business uniqueness
create order
store idempotency result
commit
return created_responseKenapa ini penting walau ada cache:
- retry bisa terjadi sebelum cache terisi,
- write endpoint seharusnya tidak lewat shared cache,
- duplikasi harus dicegah di sumber kebenaran, yaitu backend dan database.
Interaksi read-after-write dengan edge cache
Setelah operasi write berhasil, client sering memanggil endpoint GET yang mungkin masih menyajikan data lama dari edge. Ini bukan bug auth atau bug bisnis, melainkan konsekuensi konsistensi cache.
Mitigasi:
- TTL pendek untuk resource yang sering berubah,
- revalidasi dengan
ETag, - purge/invalidation ketika write penting terjadi, jika platform mendukung,
- respons write mengembalikan representasi terbaru sehingga client tidak perlu langsung melakukan
GET.
Contoh desain kontrak API
1. Endpoint publik yang aman di-cache
GET /v1/articles/redis-at-scale
200 OK
Cache-Control: public, max-age=60, stale-while-revalidate=300
ETag: "article-redis-at-scale-v17"
Content-Type: application/json
{
"slug": "redis-at-scale",
"title": "Redis at Scale",
"body": "..."
}Karena body sama untuk semua pengguna, edge cache aman. Jika artikel diubah, versi ETag ikut berubah atau resource dipurge.
2. Endpoint profil pengguna yang tidak boleh dibagi
GET /v1/me
Authorization: Bearer <token>
200 OK
Cache-Control: private, no-cache
ETag: "user-42-profile-v3"
Content-Type: application/json
{
"id": 42,
"email": "[email protected]",
"plan": "pro"
}Respons ini boleh divalidasi ulang oleh client tertentu, tetapi tidak semestinya dibagikan oleh shared edge cache.
3. Memisahkan data publik dan state personal
GET /v1/products/123
Cache-Control: public, max-age=120, stale-while-revalidate=600
ETag: "product-123-v55"
GET /v1/me/product-flags/123
Authorization: Bearer <token>
Cache-Control: private, no-storePemisahan ini biasanya jauh lebih aman dan lebih mudah di-debug dibanding menggabungkan semua field dalam satu endpoint.
Checklist implementasi cache API aman di edge
- Klasifikasikan endpoint: publik, privat, atau campuran.
- Tetapkan default aman: request dengan auth atau cookie sesi tidak masuk shared cache kecuali ada pengecualian eksplisit.
- Definisikan cache key hanya dari komponen yang benar-benar memengaruhi representasi.
- Gunakan Cache-Control yang sesuai:
publicuntuk data publik,privateatauno-storeuntuk data sensitif. - Gunakan ETag untuk revalidasi, terutama pada resource yang sering dibaca.
- Gunakan Vary secara disiplin, bukan sebagai penutup desain yang kabur.
- Hindari personalisasi implisit pada endpoint yang akan di-cache bersama.
- Jangan cache endpoint write, webhook, callback, atau operasi yang memicu efek samping.
- Terapkan idempotency key untuk request mutasi yang mungkin diulang.
- Rencanakan invalidation untuk data yang berubah karena aksi admin, publish konten, atau update harga.
Anti-pattern yang sering muncul
- Meng-cache semua GET tanpa memeriksa apakah body dipengaruhi auth atau cookie.
- Menggunakan token mentah sebagai cache key.
- Mencampur data publik dan personal dalam satu payload lalu menandainya
public. - Mengandalkan TTL panjang tanpa
ETagatau invalidation. - Menganggap
no-cachesama denganno-store. - Menganggap retry tidak masalah karena “client jarang mengulang”.
- Menganggap bug duplikasi write adalah masalah cache, padahal akar masalahnya tidak idempoten.
Observability: membedakan bug auth, bug cache, dan duplikasi request
Tanpa observability yang tepat, tiga jenis bug ini terlihat mirip: pengguna menerima data yang salah, data terasa basi, atau resource tercipta dua kali. Anda perlu metadata yang cukup untuk membedakannya.
Apa yang sebaiknya dicatat
- request id unik per request,
- correlation id lintas edge, origin, dan worker internal,
- cache status: hit, miss, revalidated, bypass, stale,
- cache key fingerprint yang aman, bukan key mentah jika mengandung data sensitif,
- auth context minimal: anonymous, user_id hash, tenant_id, role ringkas,
- idempotency key untuk endpoint write,
- ETag request/response saat troubleshooting revalidasi.
Cara membaca gejalanya
Gejala bug auth:
- request masuk ke user atau tenant yang salah,
- origin mengembalikan body personal padahal seharusnya anonim,
- cache status mungkin hanya memperparah, tetapi akar masalah ada pada identitas/otorisasi.
Gejala bug cache:
- body benar untuk satu konteks tetapi disajikan ke konteks lain,
- cache hit terjadi pada endpoint yang semestinya bypass/private,
- versi lama tetap muncul karena invalidation gagal atau key terlalu umum.
Gejala duplikasi request:
- dua atau lebih request dengan payload sama dalam jendela waktu dekat,
- request id berbeda tetapi idempotency key sama,
- efek samping tercipta dua kali karena backend tidak mengunci operasi secara idempoten.
Debugging praktis
- Ambil satu request bermasalah dengan request id.
- Periksa apakah request membawa
Authorizationatau cookie autentikasi. - Lihat cache status di edge dan origin.
- Bandingkan cache key fingerprint untuk request yang seharusnya berbeda.
- Periksa apakah origin mengirim
Cache-Control,ETag, danVarysesuai kontrak. - Untuk operasi write, cari idempotency key dan cek apakah operasi sebelumnya sudah diproses.
Penutup
Cache API aman di edge bergantung pada kontrak yang jelas, bukan pada optimasi belakangan. Untuk data publik, gunakan Cache-Control, ETag, dan bila perlu stale-while-revalidate agar cepat sekaligus efisien. Untuk data privat, default yang aman adalah tidak menyimpannya di shared cache kecuali Anda punya cache key terisolasi dan observability yang kuat.
Jika satu hal perlu diingat: pisahkan data publik dari data personal sedini mungkin. Langkah ini menyederhanakan cache key, mencegah kebocoran auth, memudahkan invalidation, dan membuat bug duplikasi request lebih mudah dibedakan dari bug cache.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!