โ† Semua pembelajaran / Go Nol โ†’ Enterprise
Fase 10 ยท Deploy di AWS

CloudFront & S3

Untuk aplikasi yang didominasi pembaca, CloudFront adalah keputusan arsitektur paling berdampak: sebagian besar permintaan berhenti di edge dan tidak pernah menyentuh task ECS-mu sama sekali.

Intisari

  • Satu distribusi bisa punya beberapa origin: ALB untuk halaman dinamis, S3 untuk media dan aset.
  • Kebijakan kunci cache menentukan segalanya โ€” query pelacakan dan cookie yang ikut masuk kunci menghancurkan rasio hit.
  • Bucket S3 tidak pernah publik. Akses lewat Origin Access Control (OAC).
  • Unggahan langsung ke S3 dengan presigned URL โ€” jangan lewat aplikasi Go-mu.
  • AWS WAF dipasang di CloudFront: memblokir sebelum trafik menyentuh origin adalah yang termurah.

Bentuk distribusinya

CloudFront
โ”œโ”€โ”€ Default (/*)          โ†’ ALB (halaman & API)
โ”‚     kebijakan cache     : CachingDisabled untuk /api/*, kustom untuk halaman
โ”‚     kebijakan origin req: teruskan Host, cookie sesi, header yang perlu
โ”‚
โ”œโ”€โ”€ /statis/*             โ†’ S3 (bundel CSS/JS ber-hash)
โ”‚     kebijakan cache     : CachingOptimized
โ”‚     TTL                 : 1 tahun, immutable
โ”‚
โ””โ”€โ”€ /media/*              โ†’ S3 (gambar unggahan)
      kebijakan cache     : CachingOptimized
      TTL                 : 30 hari

Kunci cache: yang menentukan rasio hit

Kunci cache = host + path + (query/header/cookie yang DIPILIH)

โŒ Meneruskan semua query string:
   /artikel?utm_source=fb   โ†’ entri terpisah
   /artikel?utm_source=ig   โ†’ entri lain
   /artikel?fbclid=xyz      โ†’ entri lain lagi
   โ†’ rasio hit hancur; hampir semuanya menembus ke ALB

โœ… Daftar putih parameter yang MEMENGARUHI keluaran:
   halaman, urut, kategori    โ†’ masuk kunci
   utm_*, fbclid, gclid       โ†’ diabaikan

โŒ Meneruskan semua cookie di halaman publik:
   setiap pengunjung punya cookie analitik unik โ†’ hit rate ~0%

โœ… Teruskan cookie sesi HANYA pada perilaku cache untuk rute privat

Rasio hit di bawah 90% pada situs konten hampir selalu berarti kunci cache-nya salah. Ini pemeriksaan pertama saat trafik naik dan biaya ECS-mu ikut naik padahal seharusnya tidak (Fase 9). Perbaikannya biasanya satu kebijakan cache, bukan satu baris kode.

Halaman dinamis pun bisa di-cache

// Halaman publik yang sama untuk semua pembaca
w.Header().Set("Cache-Control",
	"public, max-age=60, s-maxage=300, stale-while-revalidate=3600, stale-if-error=86400")

// Halaman personal โ€” CloudFront TIDAK BOLEH menyimpannya
w.Header().Set("Cache-Control", "private, no-store")

Pasang private, no-store sebagai default di middleware (Fase 9), lalu longgarkan secara eksplisit hanya pada rute publik. Kesalahan ke arah sebaliknya โ€” default publik, lalu lupa memperketat satu rute โ€” berarti dasbor satu pengguna disajikan ke pengguna lain, dan itu insiden keamanan yang sungguhan.

S3 tanpa bucket publik

Origin Access Control (OAC):
  โ€ข bucket memblokir SELURUH akses publik
  โ€ข kebijakan bucket hanya mengizinkan principal cloudfront.amazonaws.com
    dari ARN distribusi TERTENTU
  โ€ข objek hanya bisa diakses lewat CloudFront
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::toko-media/*",
    "Condition": {
      "StringEquals": {
        "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/E1ABCDEF"
      }
    }
  }]
}

Bucket S3 publik adalah salah satu penyebab kebocoran data paling sering di industri ini. Nyalakan Block Public Access di tingkat akun, bukan hanya per bucket โ€” dengan begitu tidak ada bucket baru yang bisa dibuat publik secara tidak sengaja.

Unggahan langsung ke S3

// Jangan mengalirkan unggahan lewat task ECS: ia memakan memori,
// menahan goroutine, dan membatasi ukuran berkas pada memori container.
func (h *Handler) URLUnggah(w http.ResponseWriter, r *http.Request) {
	u := auth.DariContext(r.Context())

	// Nama objek dibuat SERVER โ€” jangan pernah memakai nama dari klien (Fase 6).
	kunci := fmt.Sprintf("unggahan/%d/%s.jpg", u.ID, uuid.NewString())

	req, err := h.s3presign.PresignPutObject(r.Context(), &s3.PutObjectInput{
		Bucket:        aws.String(h.bucket),
		Key:           aws.String(kunci),
		ContentType:   aws.String("image/jpeg"),
		ContentLength: aws.Int64(req.Ukuran),
	}, s3.WithPresignExpires(5*time.Minute))
	if err != nil {
		h.tulisError(w, r, err)
		return
	}

	h.tulisJSON(w, http.StatusOK, map[string]string{
		"url":   req.URL,
		"kunci": kunci,
	})
}
Alur lengkapnya:

  1. Klien meminta URL unggah   โ†’ aplikasi Go memvalidasi & membuat presigned URL
  2. Klien PUT langsung ke S3   โ†’ tidak menyentuh task ECS sama sekali
  3. S3 memicu event (opsional) โ†’ SQS โ†’ worker membuat varian ukuran (Fase 5)
  4. Klien memberi tahu aplikasi โ†’ aplikasi mencatat kunci objeknya

Presigned URL berumur pendek dan terbatas. Beri masa berlaku beberapa menit, kunci ContentType, dan batasi ContentLength โ€” kalau tidak, URL itu bisa dipakai mengunggah apa saja, seukuran apa saja, sampai kedaluwarsa.

Invalidasi

# Mahal dan lambat; 1000 path pertama per bulan gratis, sisanya ditagih.
aws cloudfront create-invalidation --distribution-id E1ABCDEF --paths "/artikel/*"

Cara yang jauh lebih baik daripada invalidasi: versioning lewat nama. Aset ber-hash nama (styles.9ea43564.css) tidak pernah perlu diinvalidasi โ€” isi baru berarti nama baru. Untuk halaman, s-maxage yang pendek plus stale-while-revalidate membuat perubahan menyebar sendiri dalam hitungan menit tanpa satu pun invalidasi.

AWS WAF

AturanMenangani
Rate-based (2000/5 menit per IP)Lapisan pertama sebelum rate limit aplikasi (Fase 6)
AWSManagedRulesCommonRuleSetPola serangan umum
AWSManagedRulesKnownBadInputsRuleSetMuatan berbahaya yang dikenal
Aturan geografisKalau layananmu memang satu negara
Bot ControlPerayap agresif; berbiaya tambahan

Memblokir di CloudFront jauh lebih murah daripada di aplikasi. Permintaan yang ditolak di edge tidak memakai task ECS, tidak memakai koneksi database, dan tidak menambah data transfer keluar dari region. Dan pastikan ALB hanya menerima trafik dari CloudFront (materi ALB) โ€” kalau tidak, semua aturan WAF-mu bisa dilewati dengan memanggil ALB langsung.

Latihan: pasang CloudFront di depan ALB dengan kebijakan cache yang mengabaikan parameter utm_*. Panggil halaman publik dua kali dan periksa header X-Cache. Lalu tambahkan ?utm_source=x dan periksa apakah ia tetap hit. Terakhir, unggah berkas lewat presigned URL dan buktikan bucket-nya tidak bisa diakses langsung.

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