Langsung ke konten
KamusNgoding
Mahir Cloudflare 7 menit baca

Optimasi Caching di Cloudflare: Tips dan Trik Pro

#cloudflare #caching #kinerja #optimasi #advanced

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:

StatusArti
HITRespons dari cache Cloudflare
MISSTidak ada di cache, ambil dari origin
EXPIREDAda di cache tapi sudah kadaluarsa
BYPASSCache sengaja dilewati
DYNAMICKonten dinamis, tidak di-cache
REVALIDATEDCache 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-override max-age khusus untuk shared cache seperti edge server Cloudflare. Jika s-maxage tidak diset, Cloudflare akan menggunakan nilai max-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:

  1. Cache Rules untuk halaman produk: s-maxage=3600
  2. Workers untuk caching API produk berdasarkan ID
  3. Content hash untuk semua aset statis
  4. 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!

Artikel Terkait