S3 & CloudFront
Setiap permintaan yang dijawab CloudFront adalah permintaan yang tidak pernah menyentuh kontainermu. Bagi aplikasi yang isinya sama untuk semua pembaca, memindahkan sebanyak mungkin ke lapisan ini mengubah kebutuhan kapasitasmu secara mendasar.
Intisari
- S3 untuk berkas yang diunggah; CloudFront di depannya, tanpa bucket yang bisa diakses publik.
- Aset ber-hash Vite boleh di-cache selamanya โ nama berubah saat isi berubah.
- CloudFront juga bisa menjadi tepi untuk halaman HTML, bukan cuma aset.
- Cache policy menentukan apa yang masuk kunci cache; cookie sesi di dalamnya menghancurkan rasio hit.
- Invalidasi ada kuotanya; TTL pendek plus
stale-while-revalidatelebih hemat dan lebih sederhana.
Laravel dan S3
FILESYSTEM_DISK=s3
AWS_DEFAULT_REGION=ap-southeast-1
AWS_BUCKET=portal-media
AWS_URL=https://cdn.portal.example.com
// Tidak ada access key โ kredensial datang dari task role ECS
$path = $request->file('sampul')->store('artikel/sampul', 's3');
$url = Storage::disk('s3')->url($path); // memakai AWS_URL โ lewat CloudFront
// Untuk berkas privat: URL bertanda tangan berumur pendek
$url = Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(15));
Setel AWS_URL ke domain CloudFront, bukan ke domain S3. Tanpa itu, setiap gambar
diambil langsung dari S3 โ tanpa cache tepi, dengan latensi yang lebih tinggi bagi pembaca yang jauh, dan
biaya transfer keluar S3 yang lebih mahal daripada lewat CloudFront.
Bucket tidak boleh publik
Block all public access : ON
Akses hanya lewat : Origin Access Control (OAC) milik distribusi CloudFront
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "HanyaLewatCloudFront",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::portal-media/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1ABCDEF"
}
}
}]
}
Dengan pola ini, bucket tidak pernah bisa dijangkau langsung. Satu-satunya jalan masuk adalah lewat CloudFront โ yang berarti semua kontrol yang kamu pasang di sana (WAF, header, cache) benar-benar tidak bisa dilewati.
Origin dan perilaku
Distribusi CloudFront: cdn.portal.example.com
โโโ Origin "s3-media" โ bucket portal-media (lewat OAC)
โโโ Origin "alb-app" โ ALB aplikasi
Behavior (urutan berpengaruh):
/media/* โ s3-media cache 1 tahun, tanpa cookie
/build/* โ alb-app cache 1 tahun, tanpa cookie
/api/* โ alb-app TANPA cache, teruskan semua header
/admin/* โ alb-app TANPA cache, teruskan cookie
default * โ alb-app cache pendek, tanpa cookie
Urutan behavior menentukan segalanya, dan yang paling spesifik harus di atas. Satu kesalahan urutan
bisa membuat /admin/* tertangkap oleh behavior default yang meng-cache โ dan halaman admin milik
satu orang tersaji ke orang lain. Perlakukan daftar ini dengan kehati-hatian yang sama seperti daftar rute.
Cache policy: kunci cache menentukan rasio hit
| Yang masuk kunci cache | Akibatnya |
|---|---|
| Tidak ada (hanya URL) | Rasio hit tertinggi |
| Query string tertentu | Wajar untuk paginasi dan filter |
Header Accept-Language | Satu salinan per bahasa |
| Cookie sesi | Satu salinan per pengguna โ rasio hit runtuh |
| Semua query string | Parameter pelacakan iklan memecah cache jadi ribuan salinan |
Ini kesalahan yang paling sering merusak rasio hit. Menyertakan semua query string berarti
/artikel/kabar-pagi?utm_source=twitter dan ?utm_source=facebook menjadi dua entri
cache berbeda untuk halaman yang isinya identik. Daftarkan secara eksplisit query string mana yang benar-benar
mengubah keluaran โ biasanya cuma page dan beberapa filter.
Meng-cache halaman HTML di tepi
// Middleware dari Fase 7, sekarang berpasangan dengan CloudFront
if (! $request->user() && $request->isMethod('GET') && $response->isSuccessful()) {
$response->header('Cache-Control',
'public, max-age=0, s-maxage=300, stale-while-revalidate=600');
}
| Direktif | Efek |
|---|---|
max-age=0 | Browser selalu bertanya โ pembaruan cepat terlihat |
s-maxage=300 | CloudFront menyimpan 5 menit โ beban servermu turun drastis |
stale-while-revalidate=600 | Tidak ada pengguna yang menunggu penyegaran |
Untuk halaman artikel yang dibaca ribuan orang, kombinasi ini berarti servermu merender halaman itu sekali setiap lima menit, bukan sekali per pembaca. Perbedaan kapasitas yang dihasilkannya tidak bisa dikejar oleh optimasi kode apa pun.
Menangani pengguna yang login
Behavior default:
Cookie yang diteruskan : tidak ada
Cache berdasarkan : URL + query string terdaftar
Kalau halaman publik dan halaman personal berbagi URL, kamu punya masalah. Halaman depan yang menampilkan "Halo, Ani" di pojok kanan tidak bisa di-cache bersama. Dua jalan keluar: (1) buat bagian personal itu diambil lewat permintaan terpisah dari JavaScript, sehingga HTML-nya sama untuk semua orang; atau (2) pisahkan URL-nya. Yang pertama biasanya lebih baik untuk situs konten โ halaman utuh tetap bisa di-cache, dan hanya potongan kecilnya yang dinamis.
Invalidasi
aws cloudfront create-invalidation \
--distribution-id E1ABCDEF \
--paths "/artikel/kabar-pagi" "/kategori/politik" "/"
| Hal | Catatan |
|---|---|
| Kuota gratis | Sejumlah path per bulan; selebihnya berbayar |
| Waktu | Beberapa menit sampai menyebar ke semua lokasi tepi |
| Wildcard | /artikel/* dihitung sebagai satu path |
| Aset ber-hash | Tidak pernah perlu diinvalidasi |
Strategi yang praktis: TTL pendek untuk sebagian besar halaman, invalidasi hanya untuk yang mendesak. Artikel yang baru dikoreksi layak diinvalidasi seketika; halaman kategori bisa menunggu lima menit. Mencoba memetakan setiap URL yang terpengaruh oleh setiap perubahan adalah pekerjaan yang tidak pernah selesai dan selalu ada yang terlewat.
Yang perlu dipantau
| Metrik | Target |
|---|---|
CacheHitRate | > 90% untuk situs konten |
OriginLatency | Latensi aplikasimu saat cache meleset |
4xxErrorRate / 5xxErrorRate | Mendekati nol |
| Byte dari origin vs total | Semakin kecil rasionya, semakin baik |
Latihan: pasang CloudFront di depan ALB, buat behavior terpisah untuk /build/*, dan
aktifkan cache halaman untuk pengunjung anonim. Lalu periksa dengan
curl -sI https://โฆ/artikel/x | grep -i x-cache dua kali berturut-turut โ yang kedua harus
berbunyi Hit from cloudfront.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.