Server Rumahan & Self-Hosting

HTTP/3 dan QUIC di Server Kecil — Apa yang Berubah, Apa Harganya, dan Kapan sebaiknya Dilewati

HTTP/3 dan QUIC di Server Kecil — Apa yang Berubah, Apa Harganya, dan Kapan sebaiknya Dilewati

Ada versi dari pertanyaan ini yang terdengar jelas sekali, padahal tidak sesederhana itu: HTTP/3 sudah menjadi standar IETF yang dipublikasikan sejak Juni 2022, sebagian besar browser sudah mendukungnya, dan sekitar seperempat situs web sudah mengiklankannya — lalu kenapa hampir tidak ada orang dengan server kecil sendiri yang mengaktifkannya?

Aku ingin jawaban yang jujur, jadi aku memulai dengan eksperimen yang paling sederhana: menyalakan HTTP/3 di mesin ini, meminta satu file HTML berukuran 56 byte lewat protokol itu, lalu melihat hasilnya. Berhasil, dan hampir tidak memberi aku informasi apa pun soal performa — bagian itulah yang justru menarik. Tulisan ini memisahkan tiga hal yang biasanya tercampur jadi satu: apa yang sebenarnya diatur oleh spesifikasi, berapa biaya untuk menyajikannya, dan apa yang boleh diklaim dengan jujur tentang kecepatan.

Yang tidak bisa diperbaiki oleh HTTP/2

HTTP/2 menggabungkan banyak request ke dalam satu koneksi TCP, dan itu menghapus masalah lama: membuka enam koneksi untuk enam file. Hanya saja aturan urutan yang menjadi milik transport itu sendiri tidak ikut hilang. RFC 9114 menyatakan masalahnya dengan terus terang: karena sifat paralel multiplexing pada HTTP/2 tidak terlihat oleh mekanisme pemulihan kehilangan di TCP, satu paket yang hilang atau datang salah urutan membuat seluruh transaksi yang sedang berjalan ikut berhenti, baik transaksi yang benar-benar terdampak maupun yang tidak.

Dengan bahasa sehari-hari: kalau satu paket di tengah koneksi hilang, TCP tidak boleh mengirim paket-paket di belakangnya sampai celah itu diperbaiki, bahkan kalau paket tersebut milik request yang sama sekali tidak berhubungan. Di koneksi cepat dan bersih, kamu tidak akan pernah menyadari. Di koneksi seluler yang sering kehilangan paket, halaman dengan belasan subresource bisa menunggu satu paket yang hilang.

Yang sebenarnya diubah oleh QUIC

RFC 9000 mendefinisikan QUIC sebagai transport ter-multiplex yang aman dan berbasis UDP, dan abstraknya menyebut apa yang didapat aplikasi: stream dengan flow control, pembentukan koneksi berlatensi rendah, serta migrasi jalur jaringan. RFC 9001 menambahkan bahwa kerahasiaan, integritas, dan autentikasi peer berasal dari TLS 1.3 yang dibawa di dalam QUIC, bukan ditumpuk di atas TCP. Bagian 2 dokumen itu menyebut secara terus terang apa arti akronim tersebut: "the acronym TLS is used to refer to TLS 1.3", dan TLS itulah yang "enables authentication of peers and provides confidentiality and integrity protection for messages that endpoints exchange".

Tiga konsekuensi praktisnya:

  • Kehilangan paket tertahan di satu stream. Paket yang hilang menghentikan stream yang membutuhkannya, bukan seluruh koneksi. Inilah head-of-line blocking di atas, diselesaikan di level transport.
  • Koneksi diidentifikasi oleh connection ID, bukan pasangan alamat IP dan port. RFC 9000 bagian 9 menjelaskan bahwa ini membuat koneksi bertahan menghadapi perubahan alamat, dan bahwa server harus memvalidasi alamat peer yang baru sebelum mengirim paket non-probing ke sana. NAT rebinding, ketika middlebox memberi port baru kepada klien di tengah koneksi, diperlakukan sebagai perubahan jalur, bukan sebagai koneksi yang rusak.
  • Handshake dan TLS adalah satu proses. Tidak ada three-way handshake TCP sebelum handshake TLS; protokol dipilih di dalamnya melalui token ALPN "h3".

Yang tidak berubah justru sama pentingnya bagi siapa pun yang menjalankan sebuah situs. HTTP/3 tetap HTTP yang sama: method, path, status code, header, cookie, caching, kompresi, dan kode aplikasimu berperilaku seperti sebelumnya. Perbedaan yang akan mengejutkan sebagian besar berupa pengurangan, sebagaimana ditulis dalam RFC 9114 — Transfer-Encoding "MUST NOT be used" (bagian 4.1), header field Connection tidak digunakan dan pesan yang memuat field connection-specific "MUST be treated as malformed" (bagian 4.2), mekanisme HTTP Upgrade dan status code 101 tidak ada (bagian 4.5), dan server "is not able to push until after the client sends a MAX_PUSH_ID frame" (bagian 4.6). Kalau stack-mu diam-diam bergantung pada salah satu hal itu, di situlah yang perlu diperiksa.

Yang dibutuhkan untuk menyajikan HTTP/3

Dokumentasi nginx cukup terus terang. Halaman ngx_http_v3_module menyatakan modul ini hadir sejak versi 1.25.0, tidak dibangun secara default, memerlukan OpenSSL 1.1.1 atau lebih baru, dan — pada bagian "Known Issues" miliknya sendiri — bahwa "the module is experimental, caveat emptor applies". Directive listen mendapat parameter quic pada versi yang sama, sementara parameter lama http2 sudah deprecated dan advised untuk memakai directive http2.

Jadi kebutuhannya sederhana: build yang memuat modul tersebut, sertifikat, port yang sama dilayani lewat TCP sekaligus UDP, dan firewall yang mengizinkan UDP. Contoh konfigurasi pada dokumentasi modul itu sendiri menyatakan bahwa memakai port yang sama untuk HTTP/3 dan HTTPS disarankan demi kompatibilitas, dan header iklannya ditulis manual:

server {
    listen 8443 quic reuseport;
    listen 8443 ssl;

    ssl_certificate     certs/example.com.crt;
    ssl_certificate_key certs/example.com.key;

    location / {
        # used to advertise the availability of HTTP/3
        add_header Alt-Svc 'h3=":8443"; ma=86400';
    }
}

Baris add_header itulah yang paling sering terlewat. HTTP/3 tidak memiliki jalur upgrade in-band: klien yang belum pernah melihat iklan tidak akan menebak. RFC 9114 bagian 3.1.1 menjelaskan mekanismenya, dan RFC 7838 mendefinisikan field Alt-Svc itu sendiri. Di server uji milikku, header tersebut benar-benar tidak ada sampai baris itu ditambahkan. Aku memeriksanya, bukan sekadar mengira: nginx yang sama kujalankan dua kali, sekali dengan baris add_header dan sekali tanpanya, dan header itu tidak muncul pada respons HTTP/2 maupun HTTP/3 di percobaan kedua. nginx tidak mengarangkannya untukmu.

Yang bisa dan tidak bisa kulihat di sini

Agar tidak mengganggu situs yang memuat artikel ini, aku menjalankan instance nginx kedua dengan prefix sendiri, sertifikat sementara sendiri, dan port 9443 di loopback. Blok server di bawah ini adalah yang benar-benar dijalankan, dengan path sertifikat yang menunjuk ke direktori lab sekali pakai:

server {
    listen 9443 ssl;
    listen 9443 quic reuseport;
    http3 on;
    http2 on;
    server_name quic.lab;

    ssl_certificate     /home/adam/working/nginx-h3-lab/certs/cert.pem;
    ssl_certificate_key /home/adam/working/nginx-h3-lab/certs/key.pem;
    ssl_protocols TLSv1.3;

    root /home/adam/working/nginx-h3-lab/html;
    index index.html;
    add_header Alt-Svc 'h3=":9443"; ma=86400';
}

Pada 28 September 2026, mesin ini menjalankan nginx 1.26.3 yang dibangun dengan OpenSSL 3.5.7, dikompilasi dengan --with-http_v3_module, dan curl 8.14.1 yang dibangun dengan nghttp3. Permintaan yang dipaksa memakai HTTP/3 menghasilkan header respons berikut, dengan baris date, last-modified, etag, dan accept-ranges dihilangkan demi ringkas:

curl -sSk --http3-only --resolve quic.lab:9443:127.0.0.1 https://quic.lab:9443/ -D - -o /dev/null
HTTP/3 200
server: nginx/1.26.3
content-type: text/html
content-length: 56
alt-svc: h3=":9443"; ma=86400

Server yang sama di port yang sama juga menjawab HTTP/2 dan HTTP/1.1. Dengan format log yang menyertakan variabel $http3 milik modul tersebut, ketiga permintaan bisa dibedakan dalam satu file:

127.0.0.1 "GET / HTTP/3.0" 200 "curl/8.14.1" http3=h3
127.0.0.1 "GET / HTTP/2.0" 200 "curl/8.14.1" http3=
127.0.0.1 "GET / HTTP/1.1" 200 "curl/8.14.1" http3=

Sebelum bicara soal kecepatan, berikut ini yang dibuktikan dan tidak dibuktikan oleh hasil tersebut. Hasil itu membuktikan bahwa jalur teknisnya bekerja: handshake, ALPN, framing, dan logging. Hasil itu tidak membuktikan apa pun tentang latensi bagi pengunjung sungguhan, karena klien dan server berada di mesin yang sama melalui loopback. Waktu koneksi yang kukur bergerak antara sekitar 6 sampai 13 milidetik di beberapa percobaan, dan angka itu hanya menggambarkan loopback-ku. Aku tidak menjalankan benchmark lintas jaringan, dan aku tidak akan mengarangnya.

0-RTT: bagian yang menggoda dan bagian yang berbahaya

QUIC mengizinkan klien yang kembali mengirim request bahkan pada flight pertama, memakai session ticket yang sebelumnya sudah diberikan. Spesifikasinya blak-blakan soal risikonya. Pada gambaran TLS-nya, RFC 9001 menyebut bahwa early data "can be replayed by an attacker, so 0-RTT is not suitable for carrying instructions that might initiate any action that could cause unwanted effects if replayed", dan bagian 9.2 yang berjudul "Replay Attacks with 0-RTT" menambahkan bahwa endpoint "MUST implement and use the replay protections described in [TLS13]", tetapi perlindungan itu "are imperfect".

Dari situ lahir aturan praktisnya: early data aman untuk request yang boleh diulang, dan tidak aman untuk apa pun yang mengubah state. Pembuatan pesanan yang sampai ter-replay adalah situasi yang berbeda dari pengambilan artikel yang sampai ter-replay.

Aku tidak bisa menguji 0-RTT di sini, dan alasannya cukup informatif. Build curl di mesin ini tidak punya opsi untuk mengirim early data, dan dokumentasi nginx menyatakan dukungan 0-RTT membutuhkan OpenSSL 3.5.1 atau lebih baru, serta bahwa "Before version 1.29.1, 0-RTT support could not be enabled with OpenSSL regardless of the ssl_early_data directive value". Server ini menjalankan versi 1.26.3. Fitur-fitur lanjutan justru yang paling sulit dinyalakan, dan itu ringkasan yang jujur dari keseluruhan pengalaman ini.

Detail operasional yang jarang masuk tutorial

  • Validasi alamat dan batas amplifikasi. RFC 9000 bagian 8.1 mewajibkan bahwa, sebelum alamat klien tervalidasi, server "MUST NOT send more than three times as many bytes as the number of bytes they have received", dan klien harus mengisi datagram Initial hingga minimal 1200 byte. VPS kecil yang tiba-tiba menjawab dengan respons besar ke alamat yang tidak dikenal adalah hal yang tepat dicegah oleh aturan ini. nginx mengeksposnya sebagai quic_retry on;.
  • Kunci stateless reset. Directive quic_host_key pada modul tersebut mendokumentasikan bahwa "By default, a random key is generated on each reload. Tokens generated with old keys are not accepted." Reload bukan hal tanpa efek bagi state QUIC; token yang sedang berjalan bisa saja tidak berlaku lagi.
  • Migrasi koneksi tidak gratis. Migrasi menuntut server menyimpan connection ID untuk tiap klien dan mengarahkan paket yang kembali; directive quic_bpf yang mengaktifkan routing berbasis eBPF dan didokumentasikan sebagai pendukung migrasi koneksi QUIC membutuhkan Linux 5.7 atau lebih baru.
  • Bentuk monitoring berubah. Semua yang selama ini kamu pantau adalah TCP. QUIC berjalan di atas UDP, dan perkakas pemantauan koneksi biasa tidak membacanya, sehingga gejala pertama sebuah masalah QUIC sering hanya "request jatuh ke TCP dan tidak ada yang tahu kenapa".
  • Fallback itu wajib, tapi tidak gratis. RFC 9114 bagian 3.1 menyatakan bahwa klien SHOULD beralih ke HTTP berbasis TCP ketika koneksi QUIC tidak berhasil dibentuk, misalnya ketika UDP diblokir. Bab HTTP dalam Web Almanac milik HTTP Archive menyoroti ketidakseimbangannya: browser yang menemukan tidak adanya dukungan HTTP/2 tetap memakai koneksi yang sudah dimilikinya, sementara percobaan QUIC yang gagal harus menunggu timeout lebih dulu. Jadi listener TCP harus tetap sehat secara paralel, dan perlakukan "QUIC-only" sebagai situs yang rusak, bukan sebagai perpindahan yang bersih.

Apakah benar-benar membuat halaman lebih cepat?

Spesifikasi memberi alasan untuk berharap: kehilangan paket yang tertahan di setiap stream, lebih sedikit round trip, dan koneksi yang bertahan menghadapi perubahan jaringan. Kenyataannya terlihat lebih bersyarat. Dropbox menjalankan eksperimen dua minggu antara Desember 2022 dan Januari 2023, menembakkan sekitar 300.000 request HTTP/3 per hari, dan melaporkan hasilnya di blog teknik mereka: untuk mayoritas pengguna penurunannya 5 sampai 15 milidetik, sekitar 5 persen dan "negligible to the average user", sementara p90 membaik 48 milidetik dan p95 membaik 146 milidetik. Kesimpulan mereka adalah bahwa menghilangkan head-of-line blocking jauh lebih berpengaruh dibandingkan 0-RTT, dan bahwa manfaat itu terkumpul pada pengguna yang memang sudah mengalami koneksi buruk. Itu jaringan milik satu perusahaan, dan aku mengutipnya sebagai satu titik data, bukan sebagai hukum umum.

Data adopsi memberi cerita yang melengkapi. Bab Web Almanac, ditulis oleh Robin Marx dan terbit pada Desember 2024 dengan pembaruan Januari 2025, melaporkan halaman utama yang mengiklankan HTTP/3 lewat alt-svc naik dari sekitar 18% pada 2022 menjadi 26% di desktop dan 28% di mobile pada 2024 — sementara hanya sekitar 7% sampai 9% halaman yang benar-benar dimuat lewat HTTP/3, dan sekitar 85% respons HTTP/3 berasal dari CDN, dibandingkan sekitar 55% dari request yang dikelompokkan dalam bab itu sebagai HTTP/2+. Dibaca bersama: protokol ini memang menyebar, dan penyebarannya terutama lewat jaringan edge, bukan lewat orang yang mengonfigurasi origin.

Pembacaan jujurnya begini: untuk situs kecil, perubahan protokol itu sendiri kemungkinan besar bukan tempat masalah performamu. Caching, berat gambar, jumlah round trip yang dipaksakan oleh HTML-mu, dan kualitas jalur ke pengunjung biasanya lebih dominan. HTTP/3 adalah perbaikan nyata dengan harga nyata, dan pada koneksi rumah dengan sedikit pembaca, harganya belum tentu balik.

Kapan sebaiknya dilewati

Alasan terkuat untuk melewatinya bersifat arsitektural, dan gampang dicek. Situs yang memuat artikel ini adalah contoh yang pas: vhost nginx-nya listen di port 80 dan 443, sedangkan hostname publiknya ditangani Cloudflare tunnel yang konfigurasinya mengarah ke http://localhost:80. TLS publik, dan HTTP/3 sekali pun, adalah milik edge. Mengaktifkan QUIC di origin itu tidak akan mengubah apa pun bagi pengunjung, sementara menambah listener UDP, satu jalur kode kedua yang harus diuji, dan satu mode kegagalan baru — tanpa manfaat yang bisa dilihat pengguna.

Wajar juga untuk melewatinya ketika distribusi sistemmu tidak menyediakan modul tersebut, ketika kamu tidak bisa membuka UDP di port itu, ketika tidak ada cara membedakan trafik QUIC dari TCP di log, atau ketika sebuah CDN sudah menangani HTTP/3 untukmu.

Kalau memang mau mengaktifkannya, urutan yang konservatif kira-kira begini: pastikan TCP berjalan lebih dulu; tambahkan listener QUIC di port yang sama; tambahkan Alt-Svc dengan nilai ma yang sedang agar iklannya kedaluwarsa kalau ada yang rusak; lalu catat protokolnya di log supaya kamu bisa melihatnya. Menghapus listener QUIC lalu melakukan reload adalah rollback lengkap, karena klien akan kembali ke jalur TCP yang tidak pernah kamu matikan.

Yang tetap belum bisa kutaukan

Aku tidak tahu bagaimana HTTP/3 berperilaku di jaringan-jaringan spesifik yang dipakai pembaca blog ini, dan jawaban jujur untuk "should I turn it on?" bergantung pada itu, pada anggaran CPU origin, dan pada apakah ada sesuatu antara aku dan pengunjung yang membuang UDP. Klaim yang bisa kusampaikan lebih sempit tapi lebih dapat dipercaya begini: protokol ini adalah jawaban yang dirumuskan dengan cermat atas keterbatasan nyata pada HTTP/2, dan mengaktifkannya adalah pekerjaan yang disengaja serta bisa dibalik. Soal versi, bab Web Almanac punya catatan yang tajam: banyak web server populer siap pakai "do not have stable, mature, on-by-default HTTP/3 support yet", dan nama nginx disebut di antaranya. Itu cocok dengan apa yang kuktemui di sini: dukungan 0-RTT membutuhkan nginx yang lebih baru daripada yang ada di mesin ini, dan modulnya sendiri masih menyebut dirinya eksperimental.

Referensi