← Semua pembelajaran / Astro Nol → Portal Berita
Fase 8 · Cloudflare: Cache, Edge & WAF

Cache Rules — membuat HTML ter-cache

Cache Everything adalah pengaturan yang paling menguntungkan dan paling berbahaya di Cloudflare. Urutan pemasangannya menentukan apakah ia menghemat uang atau membocorkan data member.

Intisari

  • Pasang aturan bypass untuk jalur personal sebelum aturan cache — urutan aturan menentukan.
  • Cache Rules mengeksekusi dari atas ke bawah; yang pertama cocok yang menang.
  • Edge TTL bisa mengikuti header origin atau menimpanya. Ikuti header origin — supaya kendalinya di kodemu.
  • Browser TTL disetel terpisah dan sering terlupa.
  • Uji tiap aturan dengan curl sebelum dan sesudah, termasuk dengan cookie sesi.

Urutan aturan — ini bagian yang menentukan

Aturan 1  BYPASS   jalur personal & mutasi
Aturan 2  BYPASS   ada cookie sesi DAN jalur tidak boleh dibagi
Aturan 3  CACHE    aset ber-hash        — TTL 1 tahun
Aturan 4  CACHE    gambar & media       — TTL 30 hari
Aturan 5  CACHE    halaman publik       — ikuti header origin
Aturan 6  BYPASS   sisanya (default aman)

Aturan bypass harus di atas. Cloudflare mengeksekusi Cache Rules berurutan dan berhenti pada yang pertama cocok. Kalau aturan "cache semua halaman" berada di atas aturan "jangan cache /akun", maka dashboard member akan ter-cache — dan pembaca berikutnya yang membuka /akun akan melihat data orang lain. Ini bukan skenario teoretis; ini kegagalan yang paling sering terjadi saat orang menyalakan Cache Everything.

Aturan 1 — jalur yang tidak pernah boleh di-cache

Ekspresi:
(starts_with(http.request.uri.path, "/akun")
 or starts_with(http.request.uri.path, "/langganan")
 or starts_with(http.request.uri.path, "/admin")
 or starts_with(http.request.uri.path, "/api/midtrans-webhook")
 or starts_with(http.request.uri.path, "/_actions")
 or starts_with(http.request.uri.path, "/_server-islands")
 or http.request.method ne "GET")

Aksi: Bypass cache

/_server-islands di daftar itu penting: island paywall dirender per pembaca, dan meng-cache-nya di edge berarti pembaca pertama yang berlangganan akan membuat artikel penuh tersaji ke semua orang sesudahnya.

Aturan 2 — pembaca yang sudah login

Ekspresi:
(http.cookie contains "__Host-sesi")

Aksi: Bypass cache

Ini pilihan yang konservatif, dan ada harganya. Setiap pembaca yang login akan memukul origin untuk semua halaman — termasuk halaman artikel yang sebenarnya identik untuk semua orang. Kalau membermu banyak, ini membuang sebagian besar keuntungan cache.

Pilihan yang lebih baik, setelah kamu yakin: hapus aturan ini, dan andalkan arsitektur server island — halaman artikel bersih dari data personal, jadi aman di-cache meski pembacanya login. Tapi pasang aturan ini dulu, verifikasi seluruh jalur personal sudah tertangani aturan 1, baru lepaskan. Urutannya: aman dulu, cepat kemudian.

Aturan 3 — aset ber-hash

Ekspresi:
(starts_with(http.request.uri.path, "/_astro/"))

Aksi: Eligible for cache
  Edge TTL     : 1 year
  Browser TTL  : 1 year

Nama berkasnya mengandung hash isi, jadi isi baru selalu berarti nama baru. Ini satu-satunya kategori yang boleh di-cache selamanya di kedua lapis tanpa risiko apa pun.

Aturan 5 — halaman publik

Ekspresi:
(starts_with(http.request.uri.path, "/berita")
 or starts_with(http.request.uri.path, "/perusahaan")
 or starts_with(http.request.uri.path, "/data")
 or http.request.uri.path eq "/")

Aksi: Eligible for cache
  Edge TTL     : Use cache-control header from origin
  Browser TTL  : Respect origin

"Use cache-control header from origin" adalah pilihan yang benar, bukan TTL tetap. Ia membuat keputusan TTL hidup di kodemu, di sebelah kode yang tahu apa yang dirender — beranda 60 detik, artikel 300 detik, data perusahaan 900 detik. Menyetel TTL tetap di Cloudflare berarti dua tempat yang harus disepakati, dan salah satunya akan basi.

Kode yang menyertainya

// src/lib/cache.ts
type Profil = "beranda" | "artikel" | "kategori" | "data" | "personal";

const TTL: Record<Profil, { s: number; swr: number } | null> = {
  beranda:  { s: 60,  swr: 86400 },
  artikel:  { s: 300, swr: 86400 },
  kategori: { s: 180, swr: 86400 },
  data:     { s: 900, swr: 86400 },
  personal: null,
};

export function setCache(res: Response, profil: Profil, tag?: string[]) {
  const t = TTL[profil];

  if (t === null) {
    res.headers.set("Cache-Control", "private, no-store");
    return;
  }

  res.headers.set(
    "Cache-Control",
    `public, max-age=0, s-maxage=${t.s}, stale-while-revalidate=${t.swr}, stale-if-error=${t.swr}`,
  );

  if (tag?.length) {
    res.headers.set("Cache-Tag", tag.join(","));
  }
}
---
import { setCache } from "@/lib/cache";

const artikel = await ambilArtikelPublik(Astro.params.slug!);
if (!artikel) return Astro.rewrite("/404");

setCache(Astro.response, "artikel", [
  `artikel-${artikel.id}`,
  `kategori-${artikel.kategoriSlug}`,
]);
---

Satu fungsi berarti tidak ada halaman yang bisa menyetel TTL dengan cara yang berbeda, dan mengubah kebijakan cache adalah satu suntingan.

Jangan cache respons yang salah

// 404 boleh di-cache singkat; 500 tidak boleh sama sekali.
if (!artikel) {
  Astro.response.headers.set("Cache-Control", "public, max-age=0, s-maxage=60");
  return Astro.rewrite("/404");
}

Meng-cache 500 adalah cara mengubah gangguan sepuluh detik jadi gangguan lima menit. Dan meng-cache 404 terlalu lama berarti artikel yang baru diterbitkan tetap menampilkan "tidak ditemukan" bagi pembaca yang sempat membuka URL-nya lebih awal — yang di portal berita terjadi setiap kali tautan bocor sebelum embargo. Enam puluh detik cukup untuk melindungi origin tanpa merusak apa pun.

Query string

Cloudflare secara bawaan memasukkan SELURUH query string ke cache key.

/berita/halo                          → entri 1
/berita/halo?utm_source=facebook      → entri 2
/berita/halo?utm_source=twitter       → entri 3
/berita/halo?fbclid=IwAR…             → entri 4 … dan seterusnya, tak terbatas

Untuk portal berita yang tautannya tersebar di media sosial, ini menghancurkan cache hit ratio: setiap pembagian menghasilkan URL unik dengan parameter pelacakan. Perbaikannya lewat Cache Key: abaikan parameter yang tidak memengaruhi isi.

Cache Key → Query String → Ignore query string parameters:
  utm_source, utm_medium, utm_campaign, utm_term, utm_content,
  fbclid, gclid, igshid, ref, source

Ini sering jadi perbaikan tunggal terbesar untuk portal berita. Satu artikel viral yang dibagikan di lima platform bisa menghasilkan puluhan ribu URL unik di cache — semuanya isi yang sama persis, semuanya MISS pada kunjungan pertama. Mengabaikan parameter pelacakan menyatukannya jadi satu entri.

Verifikasi tiap aturan

U=https://portal.contoh.id

# Halaman publik: MISS lalu HIT
curl -sI $U/berita/artikel-uji | grep -i cf-cache-status
curl -sI $U/berita/artikel-uji | grep -i cf-cache-status

# Parameter pelacakan tidak boleh membuat entri baru
curl -sI "$U/berita/artikel-uji?utm_source=fb" | grep -i cf-cache-status   # harus HIT

# Jalur personal: selalu BYPASS
curl -sI $U/akun | grep -i cf-cache-status

# Server island: selalu BYPASS
curl -sI $U/_server-islands/KonteksPembaca | grep -i cf-cache-status

# Dengan cookie sesi
curl -sI $U/berita/artikel-uji -H 'Cookie: __Host-sesi=xxx' | grep -i cf-cache-status

Latihan: pasang keenam aturan dalam urutan di atas di zona staging kalau ada, atau di jam paling sepi kalau tidak. Jalankan seluruh perintah verifikasi. Lalu pasang pengabaian parameter pelacakan dan amati cache hit ratio di Analytics selama 24 jam — perubahan dari perbaikan ini biasanya terlihat dalam hitungan jam.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.