Optimasi Caching di Cloudflare: Tips dan Trik Pro
Pendahuluan
Pernahkah kamu membuka sebuah website dan merasakannya lambat padahal sudah menggunakan Cloudflare? Kemungkinan besar, konfigurasi caching-nya belum optimal. Cloudflare bukan sekadar CDN biasa — ia memiliki sistem caching yang sangat powerful jika kamu tahu cara menggunakannya dengan benar.
Bayangkan kamu sedang membangun layanan seperti Tokopedia atau Shopee: jutaan request masuk setiap detik. Tanpa caching yang tepat, origin server kamu akan kewalahan. Di artikel ini, kita akan menggali strategi caching Cloudflare dari level dasar hingga teknik pro yang digunakan dalam produksi nyata.
Memahami Mekanisme Cache Cloudflare: Dari Dasar hingga Lanjutan
Cloudflare memiliki lebih dari 300 data center (PoP — Point of Presence) di seluruh dunia. Ketika seseorang mengakses website-mu, Cloudflare menentukan apakah respons bisa dilayani dari cache-nya atau harus diteruskan ke origin server.
Alur kerja cache Cloudflare:
User → Cloudflare Edge (PoP) → [Cache HIT?]
↓ YES → Kirim dari cache (cepat!)
↓ NO → Fetch dari Origin → Simpan ke cache → Kirim ke user
Status cache yang perlu kamu pahami:
| Status | Arti |
|---|---|
HIT | Respons dari cache Cloudflare |
MISS | Tidak ada di cache, ambil dari origin |
EXPIRED | Ada di cache tapi sudah kadaluarsa |
BYPASS | Cache sengaja dilewati |
DYNAMIC | Konten dinamis, tidak di-cache |
REVALIDATED | Cache diperbarui dari origin |
Kamu bisa melihat status ini melalui response header CF-Cache-Status. Periksa dengan:
curl -I https://example.com/assets/style.css
# Output: CF-Cache-Status: HIT
Default caching Cloudflare berdasarkan ekstensi file:
Secara default, Cloudflare hanya men-cache file statis seperti .css, .js, .png, .jpg, .woff2. File HTML tidak di-cache secara default — ini penting untuk diingat.
Mengoptimalkan Cache-Control dan Expires Headers di Origin Server
Header Cache-Control dari origin server-mu adalah instruksi utama yang dibaca Cloudflare. Konfigurasi yang salah akan membuat cache tidak bekerja optimal.
Contoh konfigurasi di Nginx:
# /etc/nginx/sites-available/example.conf
server {
# Cache aset statis selama 1 tahun
location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff2|svg)$ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Cache halaman HTML selama 5 menit
location ~* \.html$ {
expires 5m;
add_header Cache-Control "public, max-age=300, s-maxage=600";
}
# Jangan cache API endpoints
location /api/ {
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}
Perbedaan max-age vs s-maxage:
max-age: Durasi cache di browser pengguna (dalam detik)s-maxage: Durasi cache di CDN/proxy termasuk Cloudflare — nilai ini meng-overridemax-agekhusus untuk shared cache seperti edge server Cloudflare. Jikas-maxagetidak diset, Cloudflare akan menggunakan nilaimax-age.
# Contoh: Browser cache 5 menit, Cloudflare cache 1 jam
add_header Cache-Control "public, max-age=300, s-maxage=3600";
Dengan konfigurasi di atas, browser pengguna akan menyimpan cache selama 5 menit, sedangkan edge server Cloudflare akan menyimpannya selama 1 jam penuh. Ini berguna untuk konten yang sering berubah di sisi pengguna tetapi tidak terlalu sering di level CDN.
Konfigurasi di Express.js (Node.js):
// app.js — konfigurasi cache headers di Express
const express = require('express');
const path = require('path');
const app = express();
// Middleware untuk mengatur cache headers pada aset statis
app.use('/static', (req, res, next) => {
res.set({
'Cache-Control': 'public, max-age=31536000, immutable',
'Vary': 'Accept-Encoding'
});
next();
});
app.use('/static', express.static(path.join(__dirname, 'public')));
// API route — jangan di-cache
app.get('/api/data', (req, res) => {
res.set('Cache-Control', 'no-store');
res.json({ data: 'fresh data', timestamp: Date.now() });
});
// Halaman HTML — cache singkat
app.get('/produk/:id', (req, res) => {
res.set('Cache-Control', 'public, max-age=300, s-maxage=3600');
res.send(`<html><body>Produk ${req.params.id}</body></html>`);
});
app.listen(3000, () => console.log('Server berjalan di port 3000'));
Kustomisasi Perilaku Cache dengan Cache Rules
Cache Rules (sebelumnya dikenal sebagai Page Rules) adalah cara paling fleksibel untuk mengontrol perilaku cache Cloudflare tanpa mengubah kode origin server. Ini bisa dikonfigurasi di dashboard Cloudflare → Rules → Cache Rules.
Contoh konfigurasi via Cloudflare API:
// create-cache-rules.js — jalankan dengan: node create-cache-rules.js
const createCacheRule = async (zoneId, apiToken) => {
const response = await fetch(
`https://api.cloudflare.com/client/v4/zones/${zoneId}/rulesets`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${apiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Cache Rules Optimasi',
kind: 'zone',
phase: 'http_request_cache_settings',
rules: [
{
description: 'Cache halaman produk selama 1 jam',
expression: '(http.request.uri.path matches "^/produk/")',
action: 'set_cache_settings',
action_parameters: {
cache: true,
edge_ttl: {
mode: 'override_origin',
default: 3600 // 1 jam dalam detik
},
browser_ttl: {
mode: 'override_origin',
default: 300 // 5 menit
}
}
},
{
description: 'Bypass cache untuk halaman login',
expression: '(http.request.uri.path eq "/login")',
action: 'set_cache_settings',
action_parameters: {
cache: false
}
}
]
})
}
);
const result = await response.json();
if (result.success) {
console.log('Cache Rules berhasil dibuat:', result.result.id);
} else {
console.error('Gagal membuat Cache Rules:', result.errors);
}
return result;
};
// Jalankan fungsi
const ZONE_ID = process.env.CF_ZONE_ID;
const API_TOKEN = process.env.CF_API_TOKEN;
if (!ZONE_ID || !API_TOKEN) {
console.error('Set environment variable CF_ZONE_ID dan CF_API_TOKEN terlebih dahulu');
process.exit(1);
}
createCacheRule(ZONE_ID, API_TOKEN);
Tips pro: Gunakan Cache Everything untuk men-cache halaman HTML yang bersifat semi-statis (misalnya halaman landing page yang jarang berubah). Ini bisa mengurangi beban origin hingga 90%.
Caching Konten Dinamis Menggunakan Cloudflare Workers
Cloudflare Workers memungkinkan kamu mengimplementasikan logika caching kustom yang tidak bisa dilakukan dengan Cache Rules biasa. Misalnya, jika ingin membangun sistem seperti Gojek yang perlu men-cache respons API berdasarkan parameter tertentu, Workers adalah solusinya.
Contoh Worker untuk caching API response:
// workers/api-cache.js
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Hanya cache GET request
if (request.method !== 'GET') {
return fetch(request);
}
// Buat cache key unik berdasarkan URL + query params
const cacheKey = new Request(url.toString(), request);
const cache = caches.default;
// Cek apakah ada di cache
let response = await cache.match(cacheKey);
if (response) {
// Tambahkan header untuk debugging
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', 'HIT-WORKER');
return new Response(response.body, {
status: response.status,
headers
});
}
// Fetch dari origin
response = await fetch(request);
// Hanya cache respons sukses
if (response.status === 200) {
// PENTING: clone SEBELUM membaca body
const responseToCache = new Response(response.clone().body, {
status: response.status,
headers: {
...Object.fromEntries(response.headers),
'Cache-Control': 'public, max-age=300', // 5 menit
'X-Cache-Status': 'MISS-WORKER'
}
});
// Simpan ke cache secara async (tidak memblokir respons)
ctx.waitUntil(cache.put(cacheKey, responseToCache));
}
return response;
}
};
Konfigurasi wrangler.toml:
name = "api-cache-worker"
main = "workers/api-cache.js"
compatibility_date = "2024-01-01"
[[routes]]
pattern = "example.com/api/*"
zone_name = "example.com"
Deploy dengan perintah:
npx wrangler deploy
Strategi Cache Busting untuk Pembaruan Aset yang Efektif
Cache busting adalah teknik untuk memaksa browser dan CDN mengambil versi terbaru dari sebuah aset. Ini krusial saat kamu deploy update baru. Konsep ini mirip dengan pola desain yang dibahas dalam Implementasi Facade Pattern di Proyek Nyata: Studi Kasus API Gateway Sederhana — keduanya soal mengelola abstraksi agar sistem tetap berjalan lancar saat ada perubahan.
Metode 1: Content Hash di nama file (Paling Direkomendasikan)
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
output: {
// Hash berubah otomatis saat konten berubah
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[chunk].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]'
}
}
}
});
// Output: main.a1b2c3d4.js, style.e5f6g7h8.css
// Saat konten berubah → hash berubah → URL baru → cache miss (otomatis update)
Metode 2: Query String Versioning
<!-- Hindari metode ini untuk aset besar — beberapa CDN mengabaikan query string -->
<link rel="stylesheet" href="/style.css?v=2.1.0">
<script src="/app.js?v=2.1.0"></script>
Metode 3: Purge Cache via Cloudflare API
# Purge URL spesifik
curl -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"files":["https://example.com/style.css","https://example.com/app.js"]}'
# Purge semua cache (gunakan dengan hati-hati!)
curl -X POST "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/purge_cache" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
Otomatisasi purge cache saat deploy (GitHub Actions):
# .github/workflows/deploy.yml
name: Deploy dan Purge Cache
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build project
run: npm ci && npm run build
- name: Deploy ke Cloudflare Pages
run: npx wrangler pages deploy dist --project-name=my-project
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
- name: Purge Cloudflare Cache
run: |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE_ID }}/purge_cache" \
-H "Authorization: Bearer ${{ secrets.CF_API_TOKEN }}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
Contoh Kasus Nyata
Skenario: Website e-commerce dengan 50.000 pengunjung/hari mengalami origin server overload.
Masalah awal:
- Semua request masuk ke origin (cache hit rate: 12%)
- Waktu respons rata-rata: 1.8 detik
- Biaya bandwidth origin: tinggi
Solusi yang diterapkan:
- Cache Rules untuk halaman produk:
s-maxage=3600 - Workers untuk caching API produk berdasarkan ID
- Content hash untuk semua aset statis
- Cache Everything untuk halaman kategori yang jarang berubah
Hasil setelah optimasi:
- Cache hit rate: 87%
- Waktu respons rata-rata: 280ms
- Beban origin berkurang 75%
Prinsip optimasi seperti ini sebenarnya mirip dengan konsep yang bisa kamu terapkan saat Menguasai Fetch API dan Async/Await di JavaScript — kedua teknik ini sama-sama soal mengelola data secara efisien agar pengalaman pengguna tetap cepat dan responsif.
Troubleshooting: Error yang Sering Muncul
Cache Selalu Menampilkan Status MISS Meskipun Sudah Dikonfigurasi
Penyebab: Origin server mengirim header Cache-Control: no-store atau Set-Cookie yang secara otomatis membuat Cloudflare melewati cache untuk semua respons.
Solusi:
# Di Nginx, hapus header Set-Cookie untuk aset statis
location ~* \.(css|js|png|jpg|woff2|svg)$ {
# Hapus cookie header agar Cloudflare mau cache
fastcgi_hide_header Set-Cookie;
proxy_hide_header Set-Cookie;
add_header Cache-Control "public, max-age=31536000, immutable";
}
Atau gunakan Cache Rule di Cloudflare untuk override header origin:
Rule: URI path matches /assets/*
Action: Override cache TTL → 86400 detik
Ignore Set-Cookie header: ON
Cache Purge via API Mengembalikan Error 1012 atau 7003
Penyebab: API token tidak memiliki permission Cache Purge, atau Zone ID yang digunakan salah (menggunakan Account ID bukan Zone ID).
Solusi:
# Langkah 1: Verifikasi Zone ID yang benar
curl -X GET "https://api.cloudflare.com/client/v4/zones?name=example.com" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
| python3 -m json.tool | grep '"id"'
# Ambil "id" pertama yang muncul — itulah Zone ID yang benar
# Langkah 2: Uji token dengan permission check
curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
# Jika status "active", token valid
# Langkah 3: Buat token baru jika perlu
# Masuk ke: Cloudflare Dashboard → My Profile → API Tokens
# Gunakan template "Cache Purge" yang sudah menyertakan permission yang tepat
Cloudflare Worker Tidak Men-cache Respons (ctx.waitUntil Tidak Berjalan)
Penyebab: Response body sudah dikonsumsi sebelum di-clone, sehingga cache.put() menerima body yang kosong dan cache tidak tersimpan.
Solusi:
// workers/api-cache-fixed.js
// SALAH — body sudah terkonsumsi sebelum di-cache
async function salah(request, ctx) {
const cache = caches.default;
const response = await fetch(request);
const data = await response.json(); // Body terkonsumsi di sini!
ctx.waitUntil(cache.put(request, response)); // Body sudah kosong!
return Response.json(data);
}
// BENAR — clone SEBELUM membaca body
async function benar(request, ctx) {
const cache = caches.default;
const response = await fetch(request);
const responseToCache = response.clone(); // Clone DULU sebelum apapun
const data = await response.json(); // Baru baca body dari response asli
ctx.waitUntil(cache.put(request, responseToCache)); // Cache dari clone
return Response.json(data);
}
export default {
fetch: benar
};
CF-Cache-Status Menampilkan BYPASS untuk Halaman Login atau Checkout
Penyebab: Ini sebenarnya perilaku yang benar dan diharapkan — Cloudflare secara cerdas mendeteksi cookie sesi dan bypass cache untuk request yang terautentikasi guna melindungi data pengguna.
Solusi:
// workers/smart-cache.js
// Jika ingin cache halaman publik saja dan bypass yang terautentikasi:
export default {
async fetch(request, env, ctx) {
const cookie = request.headers.get('Cookie') || '';
// Bypass cache untuk request terautentikasi
if (cookie.includes('session_id=') || cookie.includes('auth_token=')) {
return fetch(request);
}
// Cache halaman publik seperti biasa
const cache = caches.default;
const cached = await cache.match(request);
if (cached) return cached;
const response = await fetch(request);
if (response.status === 200) {
ctx.waitUntil(cache.put(request, response.clone()));
}
return response;
}
};
Pertanyaan yang Sering Diajukan
Apa perbedaan antara Cache Rules dan Page Rules di Cloudflare?
Page Rules adalah fitur lama Cloudflare yang sedang dalam proses penghapusan (deprecated). Cache Rules adalah penggantinya yang lebih powerful, mendukung ekspresi filter yang lebih kompleks seperti regex, dan terintegrasi penuh dengan Ruleset Engine Cloudflare. Disarankan untuk segera migrasi ke Cache Rules untuk semua konfigurasi baru agar tidak terdampak saat Page Rules resmi dihentikan.
Bagaimana cara mengecek apakah sebuah halaman sedang di-cache oleh Cloudflare?
Kamu bisa mengecek header CF-Cache-Status menggunakan perintah curl -I https://example.com/halaman-kamu. Jalankan perintah tersebut dua kali berturut-turut — request pertama akan mengembalikan MISS (mengambil dari origin), dan request kedua seharusnya mengembalikan HIT (dilayani dari cache). Jika tetap MISS setelah beberapa kali, berarti ada header di origin server yang mencegah proses caching.
Apakah caching Cloudflare aman digunakan untuk halaman yang mengandung data pengguna?
Tidak, jangan pernah men-cache halaman yang menampilkan data personal pengguna seperti dashboard, profil, atau halaman checkout. Gunakan Cache-Control: private, no-store secara eksplisit untuk halaman-halaman tersebut. Meskipun Cloudflare sudah cukup cerdas untuk bypass cache jika ada cookie sesi, mengatur header secara eksplisit memberikan lapisan perlindungan tambahan yang lebih aman.
Berapa lama maksimum TTL cache yang bisa diatur di Cloudflare?
Untuk plan Free, TTL maksimum yang bisa diatur secara manual adalah 2 jam. Untuk plan Pro, Business, dan Enterprise, kamu bisa mengatur TTL hingga 1 tahun (31.536.000 detik). Untuk aset statis dengan content hash yang bersifat immutable, nilai 1 tahun adalah pilihan paling ideal karena nama file akan otomatis berubah setiap kali kontennya diperbarui.
Mengapa cache hit rate saya rendah meskipun sudah mengatur TTL panjang?
Ada beberapa penyebab umum yang perlu diperiksa: (1) Cache baru saja di-purge sehingga belum sempat terisi ulang, (2) Origin mengirim header Vary: * yang membuat setiap request dianggap sebagai entri cache yang unik, (3) URL memiliki terlalu banyak variasi query parameter yang berbeda-beda, atau (4) Konfigurasi Cache Rule belum aktif atau belum ter-deploy dengan benar. Gunakan Cloudflare Analytics → Cache untuk melihat breakdown penyebab MISS secara mendetail.
Kesimpulan
Optimasi caching Cloudflare adalah investasi yang sangat worthwhile — dengan konfigurasi yang tepat, kamu bisa meningkatkan kecepatan website secara dramatis sekaligus mengurangi biaya dan beban origin server. Mulailah dengan mengatur header Cache-Control yang benar di origin server, lanjutkan dengan Cache Rules untuk kontrol yang lebih granular, dan gunakan Workers untuk kasus-kasus kompleks yang membutuhkan logika kustom.
Ingat: strategi caching yang baik bukan soal men-cache segalanya, tapi soal men-cache hal yang tepat dengan durasi yang tepat. Selamat bereksperimen, dan jangan ragu untuk eksplorasi artikel lainnya di KamusNgoding untuk terus memperdalam pemahamanmu — kamu pasti bisa menguasainya!