Mimpi Buruk Debugging HTTP/HTTPS: Perjalanan 24 Jam Melewati Neraka Mixed Content
Saya menghabiskan 24 jam debugging kenapa aplikasi React saya terus membuat request HTTP dari halaman HTTPS — padahal kode saya sudah mengkonversinya. Penyebabnya mengejutkan saya.
---
Update (November 2025): STRAŦUM sekarang live di Private Alpha! Platform marketing 9-agent yang disebutkan di postingan ini menerima early tester. Minta akses di stratum.chandlernguyen.com atau baca cerita peluncuran lengkapnya.
---
Konteks: Perjalanan Platform Marketing Berlanjut
Ingat postingan September di mana saya bilang saya sedang speed-run platform marketing 10-agent sambil tidur siang? Empat minggu berjalan, saya punya 3 agent yang berfungsi dan menargetkan alpha launch Oktober/November.
Nah, sekarang sudah akhir Oktober. Waktunya update progres.
Berita Baiknya:
- Platform akhirnya punya nama: STRAŦUM (Intelligence Over Execution)
- Brand guidelines dan design system lengkap (ternyata branding butuh lebih lama dari coding)
- 9 dari 10 agent dibangun dan terintegrasi
- Arsitektur multi-tenant benar-benar berfungsi
- Tahap akhir: persiapan untuk pre-alpha, testing hanya undangan
Reality Check:
Saya sakit selama 10 hari setelah cuti 8 hari (yah, hidup memang begitu.) Itu membuat segalanya tertunda dan menggeser timeline saya. Sekian untuk peluncuran Oktober itu, ya?
Tapi begini soal solo development: kamu yang mengontrol temponya. Tidak ada tekanan untuk ship ketika kamu belum siap. Tidak ada investor yang mengintimidasi. Hanya... bangun yang benar.
Postingan blog ini tentang salah satu momen "bangun yang benar" yang berubah menjadi maraton debugging 6 jam. Karena tepat ketika saya mulai kembali ke kecepatan penuh setelah sakit, platform deployment saya memutuskan punya rencana lain.
Ketika AWS Down, Engineer Jadi Kreatif (dan Kadang Menyesal)
Ini hal yang tidak diajarkan di coding bootcamp: kadang platform deployment kamu hanya... menghilang. Bukan karena sesuatu yang kamu lakukan salah, tapi karena AWS memutuskan untuk punya outage luas yang memengaruhi deployment Vercel secara global.
Begitulah Senin saya dimulai. Situs production saya down, Vercel menunjukkan error, dan saya punya tepat satu pikiran: "Saya butuh backup. Cepat."
Masuk Cloudflare Pages. Saya sudah dengar hal bagus. CDN hebat, deployment otomatis, setup sederhana. Apa yang bisa salah?
Narator: Segalanya. Segalanya bisa salah.
Perpindahan yang Terlihat Terlalu Mudah
Migrasi ke Cloudflare Pages ternyata lancar. Hubungkan repo GitHub, set environment variable di dashboard, push ke main. Tiga menit kemudian: deployed.
"Wah," pikir saya. "Ini hampir terlalu mudah."
Lalu saya buka situs production.
```
Mixed Content: The page at 'https://my-site.com/...' was loaded over HTTPS,
but requested an insecure resource 'http://stratum-api.us-central1.run.app/...'
```
Perasaan tenggelam ketika kamu sadar perayaanmu prematur? Ya, itu.
Masalahnya: HTTP Call dari Halaman HTTPS
Aplikasi React saya membuat request HTTP ke backend API sementara halamannya sendiri dimuat lewat HTTPS. Browser (dengan benar) memblokir ini sebagai risiko keamanan. Error Mixed Content. Setiap API call gagal.
"Tapi tunggu," saya bilang ke diri sendiri, "saya punya `ensureHttpsInProduction()` di kode saya! Seharusnya itu mengkonversi HTTP ke HTTPS secara otomatis!"
Saya cek bundle yang di-deploy. Fungsinya ada. Logikanya benar. Console browser menunjukkan konversi terjadi. Jadi kenapa request HTTP masih lolos?
Percobaan Debugging Pertama: Perburuan Environment Variable
Mungkin environment variable di Cloudflare tidak terbaca?
```bash
# Cek Dashboard Cloudflare
VITE_API_URL=https://stratum-api.us-central1.run.app ✓
VITE_SUPABASE_URL=https://your-project.supabase.co ✓
```
Semua HTTPS. Semua benar.
Saya trigger rebuild. Menunggu. Deploy. Buka situs.
Error yang sama. Masih request HTTP.
Percobaan Kedua: The Great Reimport
Mungkin file-file tidak menggunakan `API_BASE_URL` yang terpusat?
Saya menghabiskan satu jam berikutnya mengupdate 24 file untuk import dari `@/lib/api` alih-alih langsung menggunakan `import.meta.env.VITE_API_URL`. Setiap komponen yang membuat API call mendapat perlakuan.
```typescript
// Sebelum
const response = await fetch(`${import.meta.env.VITE_API_URL}/api/v1/...`);
// Sesudah
import { API_BASE_URL } from '@/lib/api';
const response = await fetch(`${API_BASE_URL}/api/v1/...`);
```
Push. Deploy. Tunggu.
Masih rusak.
Di titik ini, saya mulai mempertanyakan pilihan hidup saya.
Plot Twist: File yang Hilang di Git
Tapi tunggu, makin parah.
Saat menginvestigasi kenapa enforcement HTTPS saya tidak bekerja, saya menemukan sesuatu yang mengerikan. File `api.ts` yang berisi `ensureHttpsInProduction()` bahkan tidak ada di repo Git saya.
`authService.ts` juga tidak. Atau `csvSanitizer.ts`. Tiga file frontend kritis, hanya... hilang.
Kenapa? File `.gitignore` punya ini:
```
# Python stuff
lib/
build/
dist/
```
Terlihat wajar untuk Python, kan? Kecuali utility frontend saya hidup di `apps/web/src/lib/`. Pola `lib/` yang terlalu lebar secara tidak sengaja mengabaikan seluruh direktori lib frontend saya!
Ini berarti:
1. Cloudflare build dari repo (tanpa file-file ini)
2. Development lokal saya punya file-file ini (berjalan baik secara lokal)
3. Saya TIDAK TAHU file-file ini tidak ter-track
Perbaikannya:
```diff
# .gitignore - Sebelum
-lib/
# .gitignore - Sesudah
+apps/api/lib/ # Spesifik Python
+!apps/web/src/lib/ # Eksplisit include frontend lib
```
Tambahkan file yang hilang, commit, push. Sekarang Cloudflare punya kode enforcement HTTPS!
Kecuali... error HTTP terus berlanjut.
Percobaan Ketiga: Build-Time Validation
Di titik ini, saya mempertanyakan segalanya. "Tahu apa?" pikir saya. "Kalau ini terus terjadi, saya perlu MENCEGAH-nya agar tidak pernah sampai production."
Saya menulis plugin Vite yang gagalkan build jika mendeteksi URL HTTP di production:
```typescript
function validateProductionUrls(mode: string) {
if (mode !== 'production') return;
const apiUrl = process.env.VITE_API_URL || '';
if (apiUrl && apiUrl.trim().startsWith('http://')) {
if (!apiUrl.includes('localhost') && !apiUrl.includes('127.0.0.1')) {
throw new Error(
`❌ HTTPS ENFORCEMENT FAILED
Environment Variable: VITE_API_URL
Current Value: ${apiUrl}
This will cause Mixed Content errors in production!`
);
}
}
}
```
Genius, kan? Sekarang MUSTAHIL untuk deploy dengan URL HTTP.
Deploy lagi. Build lolos (environment variable sudah HTTPS). Situs dimuat.
Error. Yang. Sama.
Momen Pencerahan: File Lokal yang Ter-deploy
Larut malam, saya mendapat pencerahan.
Saya cek bundle JavaScript yang ter-deploy lagi. Benar-benar melihatnya kali ini. URL di dalamnya:
```javascript
"http://stratum-api.us-central1.run.app"
```
Tapi environment variable Cloudflare saya HTTPS. Jadi dari mana URL HTTP ini berasal?
Lalu saya sadar. File `.env.production` lokal saya.
```bash
# apps/web/.env.production (FILE LOKAL)
VITE_API_URL=http://stratum-api.us-central1.run.app
```
Cloudflare Pages men-deploy file environment lokal saya alih-alih menggunakan variable dashboard!
Saya cek `.cloudflare-pages-ignore`:
```
# Environment files
.env
.env.local
.env.development
.env.test
# .env.production ← HILANG!
```
Tepok. Jidat.
Perbaikannya: Satu Baris
```diff
# apps/web/.cloudflare-pages-ignore
.env
.env.local
.env.development
.env.test
+.env.production
```
Deploy. Tunggu.
Error berbeda kali ini! Progres!
```
Access to fetch at 'https://stratum-api.us-central1.run.app/...'
from origin 'https://preview-xyz.stratum-marketing-suite.pages.dev'
has been blocked by CORS policy
```
Error CORS! Error CORS yang indah! Itu berarti HTTPS berfungsi!
Tapi Tunggu, Masih Ada: Konspirasi Cache
Perbaiki CORS. Deploy lagi. Buka custom domain saya.
Error HTTP lagi.
Apa?!
Ternyata, CDN Cloudflare secara agresif meng-cache bundle lama. Deployment baru (dengan HTTPS) sudah live di preview URL, tapi custom domain saya menyajikan konten cache dengan URL HTTP.
Purging cache Cloudflare membutuhkan:
1. Menemukan zone settings yang tepat (tidak di dashboard Pages)
2. Navigasi melalui domain settings (tidak jelas)
3. Purge cache secara manual (untuk setiap deployment)
Setelah berjam-jam debugging masalah HTTP/HTTPS, saya membuat keputusan.
Kembali ke Vercel: Kadang Membosankan Itu Lebih Baik
AWS sudah kembali. Vercel berfungsi lagi.
Saya migrasi semuanya kembali ke Vercel. Kenapa?
1. Invalidasi cache otomatis - Tidak perlu purging manual
2. Penanganan environment variable lebih sederhana - Apa yang kamu set itulah yang kamu dapat
3. Debugging lebih cepat - Lebih sedikit infrastruktur untuk dipahami
4. Teruji - Saya tahu quirk-nya
Deployment Vercel butuh 3 menit. Tidak ada error HTTP. Tidak ada masalah cache. Hanya... berfungsi.
Yang Saya Pelajari (Dengan Cara Sulit)
1. Selalu Ignore .env.production di Platform Deployment
```
# .vercelignore
# .cloudflare-pages-ignore
# .netlify-ignore
.env
.env.local
.env.development
.env.test
.env.production ← JANGAN LUPA INI
```
2. Pola .gitignore yang Terlalu Lebar Berbahaya di Monorepo
```diff
# ❌ Buruk - Mengabaikan folder lib frontend DAN backend
-lib/
-build/
-dist/
# ✅ Bagus - Spesifik untuk setiap konteks
+apps/api/lib/ # Spesifik Python
+apps/api/build/
+apps/api/dist/
+apps/web/dist/ # Output Vite saja
```
Selalu tanya: "Bisakah pola ini secara tidak sengaja mengabaikan sesuatu yang penting?"
Di monorepo dengan multiple bahasa (Python + TypeScript), pola lebar yang dimaksudkan untuk satu ekosistem bisa secara tidak sengaja mengabaikan file kritis di ekosistem lain.
3. Build-Time Validation Tetap Layak
Meskipun tidak menangkap masalah file lokal, validasi build-time mencegah miskonfigurasi masa depan:
```typescript
// vite.config.ts
export default defineConfig(({ mode }) => {
validateProductionUrls(mode);
return {
// ... config
};
});
```
4. Pertahanan Multi-Layer Bekerja
Arsitektur final kami punya TIGA layer:
- Build-time: Gagalkan build jika URL HTTP terdeteksi
- Runtime: Konversi HTTP → HTTPS jika halaman dimuat lewat HTTPS
- Deployment: Kecualikan file .env lokal
5. Preview URL Adalah Temanmu
Selalu test di preview URL dulu. Kalau itu berfungsi tapi custom domain tidak, biasanya masalah caching.
6. Kenali Quirk Platform-mu
- Vercel: Sederhana, auto-invalidate cache, environment variable "just work"
- Cloudflare Pages: CDN hebat, tapi purging cache manual dan setup lebih kompleks
Kode yang Menyelamatkan Saya
Ini fungsi `ensureHttpsInProduction()` final:
```typescript
function ensureHttpsInProduction(url: string): string {
// Hanya konversi di konteks browser ketika situs dimuat lewat HTTPS
if (typeof window !== 'undefined' && window.location.protocol === 'https:') {
// Jangan konversi URL localhost/127.0.0.1 (development lokal)
if (url.startsWith('http://') &&
!url.includes('localhost') &&
!url.includes('127.0.0.1')) {
console.warn('[API] Converting HTTP to HTTPS:', url);
return url.replace('http://', 'https://');
}
}
return url;
}
```
Dan validasi build-time di `vite.config.ts`:
```typescript
function validateProductionUrls(mode: string) {
if (mode !== 'production') return;
const apiUrl = process.env.VITE_API_URL || '';
// Cek HTTP (seharusnya HTTPS)
if (apiUrl && apiUrl.trim().startsWith('http://')) {
if (!apiUrl.includes('localhost') && !apiUrl.includes('127.0.0.1')) {
throw new Error(`
❌ HTTPS ENFORCEMENT FAILED
Environment Variable: VITE_API_URL
Current Value: ${apiUrl}
Mixed Content Error Prevention:
Browsers block HTTP requests from HTTPS pages.
Fix: Update environment variables to use HTTPS URLs.
`);
}
}
}
```
Pelajaran Nyata: Debugging Itu Pekerjaan Detektif
Ini bukan masalah coding. Ini ekspedisi arkeologi konfigurasi.
Bug sebenarnya adalah:
1. ✅ Pola `.gitignore` yang terlalu lebar mengabaikan file frontend kritis
2. ✅ `.env.production` hilang dari `.cloudflare-pages-ignore`
3. ✅ Caching CDN yang agresif menutupi perbaikan
4. ✅ Asumsi tentang prioritas environment variable
Solusi teknisnya satu baris di file `.ignore`.
Debugging-nya? Butuh 6 jam, 14 deployment, dan terlalu banyak kopi.
Apakah Worth It?
Tentu saja. Ini yang saya dapatkan:
1. Pemahaman mendalam tentang kebijakan keamanan Mixed Content
2. Build-time validation yang mencegah masalah di masa depan
3. Enforcement HTTPS multi-layer yang platform-agnostic
4. Apresiasi nyata terhadap kesederhanaan Vercel
Dan yang paling penting: cerita debugging yang bagus untuk dibagikan. :P
Git Log Menceritakan Kisahnya
```bash
2033b9a fix: add .env.production to .vercelignore
3555390 docs: migrate deployment documentation from Cloudflare Pages to Vercel
a4edb09 chore: force clean Cloudflare Pages rebuild
590c271 fix: enhance ensureHttpsInProduction logging
15d69c6 chore: force rebuild of UserProfile bundle
a1c345f fix: add .env.production to cloudflare-pages-ignore ← PERBAIKAN .ENV
635d80d docs: update documentation for Cloudflare Pages migration
edfa611 feat: add build-time HTTPS enforcement
802c1e9 fix: enforce HTTPS for all API calls across frontend
26f6b87 refactor: comprehensive .gitignore audit and cleanup
3758a9c fix: unignore frontend lib directory and add missing files ← PERBAIKAN GITIGNORE
```
Setiap commit merepresentasikan satu jam debugging di dunia lama tapi dengan Claude Code, untungnya, kecepatan saya lebih cepat. Sebuah hipotesis yang diuji. Sebuah pelajaran yang dipetik.
Untuk Engineer Lain yang Melawan Error Mixed Content
Kalau kamu membaca ini karena sedang debugging masalah yang sama, ini checklistmu:
1. Cek Environment Variable:
```bash
# Print nilai aktual yang digunakan
console.log('API URL:', import.meta.env.VITE_API_URL);
```
2. Cek Bundle yang Ter-deploy:
```bash
# Download dan cari di bundle JavaScript
curl https://your-site.com/assets/index-ABC123.js | grep "http://"
```
3. Cek File Ignore:
```bash
# Pastikan .env.production dikecualikan
cat .vercelignore
cat .cloudflare-pages-ignore
cat .netlify-ignore
```
4. Cek .gitignore (Monorepo):
```bash
# Pastikan file kritis tidak diabaikan
git ls-files apps/web/src/lib/ # Harus menunjukkan api.ts, dll.
# Kalau kosong, cek pola yang terlalu lebar
grep "^lib/" .gitignore # ❌ Terlalu lebar
grep "^apps/api/lib/" .gitignore # ✅ Spesifik
```
5. Cek Cache:
```bash
# Test di preview URL dulu
# Kalau preview berfungsi tapi production tidak = masalah cache
```
6. Tambah Build-Time Validation:
```typescript
// Cegah agar tidak pernah terjadi lagi
if (mode === 'production' && url.startsWith('http://')) {
throw new Error('HTTPS required in production!');
}
```
Checklist Migrasi yang Saya Harap Sudah Ada
Ketika berpindah platform deployment:
- [ ] List SEMUA environment variable dari platform lama
- [ ] Setup environment variable di platform baru DULU
- [ ] Tambah SEMUA file .env ke .ignore (termasuk .env.production)
- [ ] Verifikasi .gitignore tidak mengabaikan file kritis (jalankan `git ls-files` untuk cek)
- [ ] Test di preview URL sebelum custom domain
- [ ] Cek bundle yang ter-deploy untuk URL HTTP
- [ ] Verifikasi pengaturan CORS jika backend terpisah
- [ ] Dokumentasikan quirk spesifik platform
Masih Coding, Masih Belajar, Masih Merusak Sesuatu (Kadang)
Enam jam debugging untuk perbaikan satu baris. Itulah software engineering secara ringkas.
Kode yang kita ship itu penting, tentu. Tapi skill debugging yang kita kembangkan? Itulah yang membuat kita jadi engineer yang lebih baik.
Lain kali platform deployment saya down (dan pasti ada lain kali), saya akan siap. Saya punya:
- ✅ Enforcement HTTPS yang platform-agnostic
- ✅ Build-time validation
- ✅ Pemahaman lebih baik tentang prioritas environment variable
- ✅ Strategi deployment backup
Dan semoga, postingan blog ini akan menyelamatkan orang lain dari beberapa dari 6 jam itu. :)
Pernahkah kamu punya "perbaikan satu baris" yang butuh waktu lama yang memalukan untuk ditemukan? Saya ingin dengar cerita detektif debugging-mu — sengsara senang berbagi!
Salam,
Chandler





