Memanfaatkan transportasi web modern di FrankenPHP: HTTP/3 dan QUIC lewat port 443 UDP, Early Hints (status 103) untuk mempercepat load halaman, serta real-time communication menggunakan Mercure hub untuk SSE dan event broadcast ke banyak klien.

Setelah episode 17 mengukur performa, episode ini mendorong protokol ke batas modern. FrankenPHP, berkat fondasi Caddy, menawarkan tiga teknologi transport yang banyak web server belum dukung penuh: HTTP/3 (berbasis QUIC di atas UDP), Early Hints (status 103), dan komunikasi real-time via Mercure. Ketiganya bekerja tanpa satu baris pun kode aplikasi — cukup konfigurasi.
Mengapa penting? HTTP/3 mengurangi latency di jaringan yang buruk (pindah jaringan, satelit, mobile); Early Hints memangkas waktu render halaman dengan mem-bootstrap resource penting lebih awal; Mercure membuka pintu ke fitur real-time (notifikasi, live updates) tanpa memelihara infrastruktur WebSocket sendiri.
HTTP/3 adalah HTTP di atas QUIC, protokol yang berjalan di UDP (bukan TCP). Manfaatnya:
Caddy mengaktifkan HTTP/3 secara otomatis ketika port 443 UDP terbuka. Artinya di FrankenPHP, HTTP/3 sudah aktif tanpa konfigurasi — asalkan firewall membuka UDP:
sudo ufw allow 443/udp
sudo ufw allow 443/tcp
sudo ufw allow 80/tcpDi Docker, ekspos port UDP-nya:
docker run \
-p 443:443 \
-p 443:443/udp \
dunglas/frankenphpTip
Pemeriksaan: di Chrome DevTools → Network, protokol request ditampilkan sebagai h3. Di curl modern: curl --http3 https://domain. Jika belum h3, berarti port UDP 443 belum terbuka — periksa firewall/nat.
Status 103 Early Hints mengirim respons sementara sebelum respons penuh, berisi resource yang dibutuhkan lebih awal (preload). Browser langsung mulai mengunduh — tanpa menunggu HTML selesai diproses. Dampak: waktu first contentful paint membaik signifikan, terutama di halaman yang bergantung pada CSS/JS besar.
Di FrankenPHP, Early Hints tersedia di PHP:
<?php
if (function_exists('frankenphp_early_hints')) {
frankenphp_early_hints([
'Link: </static/app.css>; rel=preload; as=style',
'Link: </static/app.js>; rel=preload; as=script',
]);
}Caddy juga mendukung Early Hints pada respons statis via directive header dengan status 103:
localhost {
root * public/
header /static/* 103 Link "</static/app.css>; rel=preload; as=style"
file_server
}Urutan kerja 103: browser dapat respons 103 → mulai preload → terima respons 200 → render lebih cepat. Ini adalah "gratis" yang paling murah — beberapa baris konfigurasi, efek terukur.
Caddy mendukung WebSocket secara native melalui reverse_proxy — tanpa plugin, tanpa config khusus. FrankenPHP meneruskannya secara otomatis:
localhost {
@ws path /ws
reverse_proxy @ws ws://backend:8080
root * public/
php_server
}Upgrade header diteruskan, koneksi dipertahankan, dan biaya koneksi idle mendekati nol. Untuk aplikasi PHP murni yang butuh WebSocket server sendiri, panduan resmi merekomendasikan menjalankan server WebSocket terpisah dan meneruskan lewat reverse_proxy di atas.
Mercure adalah hub komunikasi real-time bawaan FrankenPHP — dirancang sebagai alternatif modern untuk WebSocket yang tetap kompatibel dengan HTTP/SSE. Alur kerjanya:
https://domain/.well-known/mercure?topic=/bookings.Aktifkan hub Mercure:
{
mercure {
publisher_jwt "ZXhhbXBsZQ=="
anonymous
cors_origins https://kaliansite.com
}
}
kaliansite.com {
root * public/
php_server
}Publikasikan event dari PHP — tanpa WebSocket server khusus:
<?php
$hub = 'https://kaliansite.com/.well-known/mercure';
$token = 'publisher_jwt_anda';
frankenphp_mercure_publish(
$hub,
'/bookings/new',
json_encode(['id' => 42, 'status' => 'confirmed']),
null,
null,
['Content-Type' => 'application/json'],
$token,
);frankenphp_mercure_publish() adalah helper bawaan FrankenPHP untuk mempublikasikan event dari PHP. Di sisi klien, cukup EventSource:
const es = new EventSource(
'https://kaliansite.com/.well-known/mercure?topic=/bookings/new',
);
es.addEventListener('message', (event) => {
const booking = JSON.parse(event.data);
renderBooking(booking); // update UI real-time
});Note
Mercure bekerja di atas SSE (Server-Sent Events) — satu arah (server → klien), yang mencakup mayoritas kasus (notifikasi, live feed, dashboard). Untuk komunikasi dua arah penuh, gunakan WebSocket native lewat reverse_proxy di atas. Pilih berdasarkan kebutuhan, bukan tren.
{
mercure {
publisher_jwt "ZXhhbXBsZQ=="
anonymous
cors_origins https://kaliansite.com
}
}
kaliansite.com {
root * public/
encode zstd br gzip
php_server {
# 103 Early Hints via Caddy (opsional)
}
@ws path /ws
reverse_proxy @ws ws://backend:8080
}Dengan port 443 UDP terbuka, stack ini menyajikan: HTTP/3, HTTP/2, HTTP/1.1, Early Hints, SSE (Mercure), dan WebSocket — semuanya dari satu binary.
--http3.encode dan SSE: kompresi tidak boleh menambah buffering berlebihan pada stream SSE. Jika event terasa tertunda, nonaktifkan kompresi untuk path Mercure.publisher_jwt adalah rahasia — jangan pernah taruh di Caddyfile yang masuk git; injeksi via env var (episode 11).cors_origins dengan benar.Pada episode 18 ini, kalian telah memanfaatkan transportasi web modern di FrankenPHP.
Inti yang harus dibawa pulang:
h3.reverse_proxy ws:// — tanpa plugin.frankenphp_mercure_publish(), subscribe di frontend dengan EventSource.Di episode 19 selanjutnya kita pindah ke deployment skala: Docker & Kubernetes — membangun image production yang benar, volume caddy_data, non-root, dan deploy ke Kubernetes dengan ingress + HPA. Sampai jumpa di episode 19!