Belajar FrankenPHP - Static Files, Gzip & HTTP Caching
Episode 8 of 25

Belajar FrankenPHP - Static Files, Gzip & HTTP Caching

Mengoptimalkan pengiriman aset statis di FrankenPHP: kompresi on-the-fly dengan encode zstd br gzip, menyajikan file yang sudah dikompresi lewat precompressed, header Cache-Control & ETag untuk browser caching, dan memisahkan rute aset agar tidak membebani PHP.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah dua episode terakhir fokus pada framework (Laravel dan Symfony), episode ini membahas lapisan yang menentukan kesan kecepatan pertama pengguna: pengiriman aset statis. CSS, JavaScript, gambar, dan font biasanya menyumbang mayoritas byte yang ditransfer — dan di sinilah keunggulan FrankenPHP/Caddy terasa: kompresi modern, HTTP caching, dan efisiensi file server, semuanya bawaan.

Mengapa penting? php_server secara default sudah menyalakan file server, tetapi file server yang "jalan apa adanya" tanpa kompresi dan header cache akan membuat halaman terasa lambat walau PHP-nya secepat kilat. Pengoptimalan yang kita lakukan di episode ini sering kali memberi peningkatan skor performa terbesar dengan usaha terkecil.

Kompresi: encode zstd br gzip

Caddy mendukung tiga algoritma kompresi modern sekaligus. Prioritasnya penting:

Caddyfile - aktifkan kompresi
localhost {
    encode zstd br gzip
 
    root * public/
    php_server
}

Urutan di atas berarti: jika browser mendukung Zstandard (zstd), kirim zstd; jika tidak, turun ke Brotli (br); terakhir gzip sebagai jaring pengaman universal. Zstd dan Brotli menghasilkan rasio kompresi lebih baik dengan kecepatan lebih tinggi daripada gzip untuk aset teks.

Tip

Posisi encode di Caddyfile menentukan apa yang ikut dikompresi. Letakkan di awal site block agar semua response (termasuk dari reverse_proxy) terkompresi. Untuk aset yang sudah dikompresi (gambar JPEG/PNG, video), kompresi tambahan hanya buang CPU.

Menyajikan File Pre-Compressed: precompressed

Kompresi on-the-fly menghabiskan CPU per request. Untuk produksi dengan trafik tinggi, kompresi file sekali saat build lalu biarkan Caddy menyajikan versi terkompresi tanpa kerja tambahan. Symfony AssetMapper punya dukungan built-in:

Kompilasi aset Symfony
php bin/console asset-map:compile

Ini menghasilkan app.css.zst, app.css.br, dll. Lalu Caddyfile:

Caddyfile - precompressed assets
localhost {
    @assets path /assets/*
    file_server @assets {
        precompressed zstd br gzip
    }
 
    root * public/
    php_server {
        try_files {path} index.php
    }
}

precompressed membuat Caddy memeriksa keberadaan app.css.zst/app.css.br/app.css.gz saat ada request app.css — dan mengirim versi terkompresi tanpa mengompresi apa pun saat runtime. Browser menerima Content-Encoding yang tepat sesuai kemampuan negosiasinya.

Memisahkan Aset dari PHP: Mengurangi Beban

php_server secara default memeriksa apakah request adalah file statis sebelum meneruskan ke PHP. Untuk aplikasi dengan front controller tunggal (Laravel/Symfony), kita bisa lebih agresif: aset hanya hidup di /assets, sisanya langsung ke PHP. Ini menghindari pencarian filesystem yang sia-sia di hot path.

Caddyfile - split rute aset dan PHP
localhost {
    route {
        @assets path /assets/*
        file_server @assets {
            root public/
            precompressed zstd br gzip
        }
 
        rewrite index.php
        php {
            root public/
        }
    }
}
  • Semua request /assets/* → file server dengan precompression.
  • Semua request lain → index.php langsung, tanpa cek file.
  • Directive php (bukan php_server) karena di sini kita tidak butuh file server bawaan — aset sudah ditangani sendiri.

HTTP Caching: Cache-Control dan ETag

Browser caching menentukan berapa banyak request aset yang benar-benar keluar dari server. Header Cache-Control diatur via directive header:

Caddyfile - header caching aset
localhost {
    @assets path /assets/*
    header @assets {
        Cache-Control "public, max-age=31536000, immutable"
    }
    file_server @assets
 
    root * public/
    php_server
}
  • max-age=31536000 = satu tahun. Aman untuk aset dengan fingerprint nama file (misal app-abc123.css).
  • immutable memberi tahu browser bahwa isi tidak akan berubah — cegah revalidation yang tidak perlu.
  • Untuk halaman HTML (yang tidak di-hash namanya), jangan pakai cache setahun — gunakan no-cache atau cache pendek.

ETag dan Revalidation

Caddy secara default menghitung ETag (fingerprint file) dan melayani If-None-Match — browser mengirim request sangat ringan dan menerima 304 Not Modified jika file tak berubah. Ini bekerja otomatis di file_server, jadi tidak perlu konfigurasi tambahan — hanya perlu dipahami saat kalian melihat 304 di log.

Kesalahan Umum (Common Pitfalls)

  • Kompresi gambar: JPEG/PNG/WebP sudah terkompresi; encode pada mereka membuang CPU dan bisa malah memperbesar ukuran. Batasi kompresi ke tipe teks dengan matcher.
  • Cache-Control berlebihan di halaman HTML: halaman yang di-cache setahun tidak akan pernah menampilkan update. Pisahkan kebijakan aset vs halaman.
  • Lupa precompressed saat pindah ke CDN: file .zst/.br di-root asset tidak akan dikirim dengan benar jika CDN tidak tahu header Content-Encoding. Pastikan konfigurasi CDN meneruskan header tersebut.
  • file_server off pada php_server: jika kalian mematikan file server, pastikan aset dilayani jalur lain — jika tidak, semua aset 404.

Penutup

Pada episode 8 ini, kalian telah mengoptimalkan pengiriman aset statis.

Inti yang harus dibawa pulang:

  • encode zstd br gzip — kompresi modern dengan prioritas yang benar.
  • precompressed — sajikan file hasil build tanpa CPU runtime.
  • Pisahkan rute /assets/* agar PHP tidak diganggu pencarian filesystem.
  • Header Cache-Control + ETag untuk memaksimalkan browser caching.
  • Jangan kompresi ulang media yang sudah terkompresi.

Di episode 9 selanjutnya kita melebarkan horizon: reverse proxy dan load balancing — meneruskan request ke API, microservices, atau backend lain, lengkap dengan health check, load balancing policies, dan trusted_proxies. Sampai jumpa di episode 9!

Belajar FrankenPHP - Static Files, Gzip & HTTP Caching | Belajar FrankenPHP