Statis vs on-demand — memilih model render
Astro memberimu tiga pilihan per halaman, bukan satu pilihan untuk seluruh situs. Untuk portal berita, memilih dengan benar per halaman itulah seluruh permainannya.
Intisari
output: "static"adalah default. Tiap halaman dirender saat build dan dilayani sebagai berkas.output: "server"membalik default-nya: semua on-demand, kecuali yang kamu tandaiprerender = true.output: "hybrid"sudah tidak ada sejak Astro 5 — cukupserverplusprerenderper halaman.- Halaman statis tidak punya
Astro.requestyang berarti, dan tidak bisa membaca cookie. Ini penyebab bug "member selalu null". - Untuk portalmu:
server+ cache Cloudflare agresif mengalahkan prerender penuh, karena artikel baru langsung hidup tanpa build ulang.
Tiga model, satu tabel
| Statis (prerender) | On-demand (SSR) | On-demand + cache CDN | |
|---|---|---|---|
| Kapan dirender | Saat build | Tiap request | Request pertama, lalu dari edge |
| Bisa baca cookie | Tidak | Ya | Ya, tapi merusak cache |
| Artikel baru muncul | Setelah build ulang | Langsung | Langsung |
| Beban origin | Nol | Penuh | Sekitar 5–10% |
| Waktu build 200rb artikel | Berjam-jam | Detik | Detik |
| Cocok untuk | Halaman Tentang Kami, syarat & ketentuan | Dashboard member, keranjang | Artikel, kategori, halaman data |
Konfigurasi yang dipakai portal ini
// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
import vue from "@astrojs/vue";
export default defineConfig({
site: "https://portal.contoh.id",
output: "server",
adapter: node({ mode: "standalone" }),
integrations: [vue()],
});
---
// src/pages/tentang.astro — halaman yang tidak pernah berubah
export const prerender = true;
---
output: "hybrid" sudah dihapus. Kalau kamu menemukan tutorial yang menyuruh
memakainya, tutorial itu ditulis untuk Astro 4 ke bawah. Sejak Astro 5, server plus
export const prerender per halaman melakukan hal yang sama dengan lebih sedikit konsep.
Beberapa hal lain di tutorial lama juga sudah berubah: <ViewTransitions /> menjadi
<ClientRouter />, dan content collections wajib memakai Content Layer API.
Jebakan yang paling sering: halaman statis tidak punya request
---
// TIDAK BEKERJA di halaman prerender. Diam, tanpa error.
const sesi = Astro.cookies.get("sesi");
const member = sesi ? await ambilMember(sesi.value) : null;
---
{member ? <p>Halo, {member.nama}</p> : <a href="/masuk">Masuk</a>}
Di halaman yang di-prerender, kode ini berjalan saat build — jauh sebelum ada pembaca. Tidak ada
cookie untuk dibaca, jadi member selalu null, dan setiap pembaca melihat tautan
"Masuk" termasuk yang sudah login. Ia tidak melempar error; ia hanya salah.
Astro akan memperingatkanmu kalau kamu memakai API yang jelas-jelas butuh request — tapi peringatan itu mudah tenggelam di antara keluaran build lain. Yang lebih dapat diandalkan adalah kebiasaan: apa pun yang bergantung pada siapa pembacanya tidak boleh ada di halaman yang di-cache, statis maupun on-demand-yang-di-cache. Tempatnya di server island.
Kenapa on-demand + CDN, bukan prerender penuh
Untuk portal dengan 200 ribu artikel dan puluhan artikel baru per hari, prerender penuh berarti:
- Build berjam-jam, jadi kamu hanya sanggup build beberapa kali sehari.
- Artikel breaking news menunggu build berikutnya — tidak bisa diterima di ruang redaksi.
- Perbaikan ketik pada satu artikel butuh membangun ulang seluruh situs.
- Artefak build ratusan ribu berkas yang harus disalin ke tiap task ECS setiap deploy.
On-demand dengan cache edge memberi hasil yang menyerupainya dari sisi pembaca — HTML dari edge, tanpa menyentuh origin — tapi artikel baru hidup begitu editor menekan Terbitkan, dan perbaikan cukup purge satu cache tag. Ini kombinasi yang dipakai hampir semua portal berita besar, dan yang akan kita bangun di Fase 8.
Header cache yang membuatnya bekerja
---
export const prerender = false;
const artikel = await ambilArtikelPublik(Astro.params.slug!);
if (!artikel) return Astro.rewrite("/404");
// Cache di edge 5 menit, boleh disajikan basi 1 hari sambil disegarkan.
Astro.response.headers.set(
"Cache-Control",
"public, max-age=0, s-maxage=300, stale-while-revalidate=86400",
);
// Supaya bisa di-purge per artikel saat editor menyunting.
Astro.response.headers.set("Cache-Tag", `artikel-${artikel.id},kategori-${artikel.kategoriSlug}`);
---
| Arahan | Untuk siapa | Artinya |
|---|---|---|
max-age=0 | Browser pembaca | Jangan simpan; selalu tanya. Supaya koreksi artikel langsung terlihat. |
s-maxage=300 | Cloudflare | Simpan 5 menit. Ini yang menyerap trafiknya. |
stale-while-revalidate=86400 | Cloudflare | Setelah kedaluwarsa, sajikan versi lama sambil mengambil yang baru di latar. Pembaca tidak pernah menunggu origin. |
Cache-Tag | Cloudflare | Label untuk purge selektif saat artikel disunting. |
stale-while-revalidate yang panjang adalah jaring pengaman produksimu. Kalau ECS
tumbang, Cloudflare tetap menyajikan artikel basi selama sehari, bukan halaman error. Untuk portal
berita, artikel yang terlambat lima menit jauh lebih baik daripada 502. Tambahkan
stale-if-error untuk memperjelas maksudnya.
Peta keputusan untuk portalmu
| Halaman | Model | Alasan |
|---|---|---|
| Beranda | On-demand, s-maxage=60 | Berubah sering, dibaca sangat banyak |
| Artikel | On-demand, s-maxage=300 + tag | Jarang berubah setelah terbit; purge saat disunting |
| Kategori & arsip | On-demand, s-maxage=300 | Sama |
| Data perusahaan | On-demand, s-maxage=900 | Sumber datanya diperbarui harian |
| Blok paywall | Server island, private, no-store | Per pembaca — tidak boleh di-cache bersama |
| Dashboard member | On-demand, private, no-store | Seluruh halamannya personal |
| Tentang, S&K, kebijakan privasi | prerender = true | Berubah setahun sekali |
| Webhook Midtrans | Endpoint, tanpa cache | POST, dan tidak boleh di-cache di lapisan mana pun |
Latihan: setel output: "server", lalu buat dua halaman: satu dengan
prerender = true dan satu tanpa. Di kedua halaman, cetak
Astro.request.headers.get("user-agent"). Jalankan pnpm build dan perhatikan
bedanya di dist/ — satu jadi berkas .html, satu tidak. Lalu jalankan
pnpm preview dan bandingkan apa yang tercetak di masing-masing halaman.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.