1. Evolusi HTTP: Dari 1.0 ke 3.0
HTTP (HyperText Transfer Protocol) telah berevolusi secara dramatis sejak pertama kali diusulkan oleh Tim Berners-Lee pada tahun 1991. Pemahaman tentang evolusi ini membantu developer memahami mengapa HTTP/2 dan HTTP/3 dirancang seperti sekarang.
Timeline Evolusi HTTP
| Versi | Tahun | Transport | Inovasi Utama |
|---|---|---|---|
| HTTP/0.9 | 1991 | TCP | Hanya GET, satu baris request, tanpa header |
| HTTP/1.0 | 1996 | TCP | Header, status codes, metode POST/HEAD |
| HTTP/1.1 | 1997 | TCP | Persistent connections, pipelining, chunked encoding |
| HTTP/2 | 2015 | TCP + TLS | Binary framing, multiplexing, server push, HPACK |
| HTTP/3 | 2022 (RFC 9114) | QUIC (UDP) | 0-RTT, connection migration, QPACK, independent streams |
Masalah HTTP/1.1 yang Dipecahkan
# Masalah 1: Head-of-Line Blocking di HTTP/1.1 # Setiap request/response harus tunggu giliran Client Server |--- Request A ------->| | | |<-- Response A --------| β Harus selesai dulu | | |--- Request B ------->| β Baru bisa kirim B | | |<-- Response B --------| # Masalah 2: Pipelining yang tidak reliable # Browser men-disable pipelining karena masalah di proxy lama Client Server |--- Request A ------->| |--- Request B ------->| β Dikirim tanpa tunggu |--- Request C ------->| | | |<-- Response A --------| β HARUS berurutan |<-- Response B --------| β Bisa ter-block jika A lambat |<-- Response C --------| # Masalah 3: Connection overhead # HTTP/1.1: browser buka 6-8 koneksi TCP paralel per domain # β Memory overhead, congestion window terpisah # β TLS handshake dilakukan untuk setiap koneksi
2. HTTP/2 Fundamentals
HTTP/2 (RFC 7540, 2015) merupakan lompatan besar dalam protokol web. Berbasis pada protokol SPDY yang dikembangkan Google, HTTP/2 memperkenalkan binary framing layer di atas TCP yang memungkinkan multiplexing, flow control, dan prioritas stream.
Perubahan Paradigma
| Aspek | HTTP/1.1 | HTTP/2 |
|---|---|---|
| Format | Text-based | Binary framing |
| Header | Text, diulang tiap request | Binary, compressed (HPACK) |
| Multiplexing | Tidak (parallel TCP) | Ya, dalam satu koneksi |
| Prioritas | Tidak ada | Stream dependency + weight |
| Server Push | Tidak | Ya |
| Flow Control | Tidak | Per-stream + connection level |
| Connection | 1 request per koneksi (atau pipelining) | Ratusan stream dalam 1 koneksi |
HTTP/2 Binary Frame Format
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Length (24 bit) | +---------------+---------------+-------------------------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...2^24-1) | +===============================================================+ # Frame Types: # 0x0 DATA β Payload data # 0x1 HEADERS β Request/response headers # 0x2 PRIORITY β Stream priority # 0x3 RST_STREAM β Stream termination # 0x4 SETTINGS β Connection parameters # 0x5 PUSH_PROMISE β Server push announcement # 0x6 PING β Connection liveness # 0x7 GOAWAY β Graceful shutdown # 0x8 WINDOW_UPDATE β Flow control # 0x9 CONTINUATION β Continued header block
Connection Upgrade (h2)
HTTP/2 biasanya dinegosiasikan selama TLS handshake menggunakan ALPN (Application-Layer Protocol Negotiation). Browser modern hanya mendukung HTTP/2 di atas TLS (h2), sedangkan versi plaintext (h2c) hanya untuk spesifikasi/konfigurasi khusus.
# TLS ALPN Negotiation untuk HTTP/2 Client Server | | |--- ClientHello | | ALPN: [h2, http/1.1] -----------> | | | |<-- ServerHello | | ALPN: h2 (memilih HTTP/2) | | | |==== HTTP/2 Connection Preface =========>| | SETTINGS frame | # Connection Preface: # Client: "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + SETTINGS frame # Server: SETTINGS frame + possible ACK # Alternatif upgrade dari HTTP/1.1 (tanpa TLS): # GET / HTTP/1.1 # Host: example.com # Connection: Upgrade, HTTP2-Settings # Upgrade: h2c # HTTP2-Settings: <base64url encoded SETTINGS>
3. Multiplexing & Stream
Fitur paling revolusioner HTTP/2 adalah multiplexing β kemampuan mengirim multiple request dan response secara simultan dalam satu koneksi TCP. Setiap HTTP message dipecah menjadi frames yang di-tag dengan Stream ID, sehingga bisa di-interleave tanpa blocking.
Stream Lifecycle
# Stream States dalam HTTP/2: # # idle β open β half-closed β closed # (local/remote) # # Client-initiated streams: ODD numbers (1, 3, 5, ...) # Server-initiated streams: EVEN numbers (2, 4, 6, ...) # Multiplexing dalam aksi: Client Server | | |--- [Stream 1] HEADERS ------>| (GET /index.html) |--- [Stream 3] HEADERS ------>| (GET /style.css) |--- [Stream 5] HEADERS ------>| (GET /app.js) | | |<-- [Stream 1] DATA ----------| (HTML response, part 1) |<-- [Stream 3] DATA ----------| (CSS response, part 1) |<-- [Stream 1] DATA ----------| (HTML response, part 2) β interleaved! |<-- [Stream 5] DATA ----------| (JS response) |<-- [Stream 1] DATA END ------| (HTML selesai) |<-- [Stream 3] DATA END ------| (CSS selesai) |<-- [Stream 5] DATA END ------| (JS selesai) # Tidak ada Head-of-Line blocking antar stream! # Satu stream lambat tidak memblokir yang lain.
Stream Priorities dan Dependencies
HTTP/2 memungkinkan client memberikan informasi prioritas pada setiap stream melalui HEADERS atau PRIORITY frame. Ini membantu server mengalokasikan bandwidth secara optimal:
# Priority Tree β Contoh halaman web: # # Stream 1 (HTML) β weight: 256 # βββ Stream 3 (CSS) β weight: 220 (paling prioritas!) # βββ Stream 5 (JS) β weight: 183 # βββ Stream 7 (Image 1) β weight: 110 # βββ Stream 9 (Image 2) β weight: 110 # Priority frame: # - Exclusive flag: apakah stream eksklusif child? # - Stream Dependency: parent stream ID (0 = root) # - Weight: 1-256 (relative weight) # Strategi browser umum: # CSS β Paling tinggi (render-blocking) # HTML β Tinggi # JS β Sedang (parser-blocking tapi bisa defer) # Font β Sedang-tinggi # Image β Rendah (lazy-load)
Stream dependency tree terbukti kompleks dan sulit diimplementasikan dengan benar. RFC 9218 menggantikannya dengan sistem prioritas sederhana: urgency (0-7) dan incremental flag (boolean). Ini lebih mudah diimplementasikan oleh server dan lebih predictable.
4. Server Push
Server Push memungkinkan server mengirim resource ke client sebelum client memintanya. Ini berguna untuk resource yang server tahu akan dibutuhkan, seperti CSS, JS, dan font yang direferensikan di HTML.
Alur Server Push
Client Server | | |--- [Stream 1] GET /index.html --->| | | |<-- [Stream 2] PUSH_PROMISE -------| (Server push /style.css) | Promised Stream ID: 2 | |<-- [Stream 4] PUSH_PROMISE -------| (Server push /app.js) | Promised Stream ID: 4 | | | |<-- [Stream 1] DATA (HTML) --------| (Response untuk index.html) |<-- [Stream 2] DATA (CSS) ---------| (Pushed resource) |<-- [Stream 4] DATA (JS) ----------| (Pushed resource) # Client bisa MENOLAK push dengan RST_STREAM (CANCEL) # Pushed resource disimpan di browser cache # Jika client sudah punya di cache β RST_STREAM β hemat bandwidth
Konfigurasi Server Push di Nginx
# nginx.conf β HTTP/2 Server Push
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
# Aktifkan HTTP/2
http2_push_preload on;
location / {
# Push critical CSS
http2_push /css/main.css;
# Push critical JS
http2_push /js/app.js;
# Push font
http2_push /fonts/inter.woff2;
root /var/www/html;
index index.html;
}
# Alternatif: gunakan Link header untuk push
location /css/main.css {
add_header Link "; rel=preload; as=font";
}
}
Server Push telah di-deprecate di Chrome (sejak versi 106) dan Safari. Masalah utama: sulit menentukan resource mana yang sudah ada di cache client, sehingga sering terjadi push waste. Alternative yang lebih baik: gunakan 103 Early Hints (RFC 8297) untuk memberitahu browser resource apa yang perlu di-preload.
5. Header Compression (HPACK)
HTTP/1.1 mengirim header dalam format text yang berulang-ulang pada setiap request. Header seperti Cookie, User-Agent, dan Accept yang sama dikirim berulang kali. HPACK (RFC 7541) mengatasi ini dengan kompresi header berbasis Huffman coding dan indexing table.
Cara Kerja HPACK
# HPACK menggunakan dua tabel: # 1. Static Table (61 entries pre-defined) # Index 1: :authority # Index 2: :method GET # Index 3: :method POST # Index 4: :path / # Index 8: :status 200 # Index 13: cache-control max-age=0 # ...dst # 2. Dynamic Table (berubah selama koneksi) # Header yang dikirim client/server ditambahkan ke tabel # Index dimulai dari 62 (setelah static table) # Contoh kompresi: # Request 1: :method: GET, :path: /api/data, host: example.com, cookie: abc123 # β Encoded dengan static index + Huffman + dynamic table entry # Request 2: :method: GET, :path: /api/users, host: example.com, cookie: abc123 # β host dan cookie sudah di dynamic table β hanya kirim index reference! # Penghematan signifikan: dari ~800 bytes menjadi ~50-100 bytes per request
6. QUIC Protocol
QUIC (RFC 9000, 9001, 9002) adalah transport protocol baru yang berjalan di atas UDP, dikembangkan oleh Google dan kemudian distandarisasi oleh IETF. QUIC menggabungkan fungsi transport (TCP), keamanan (TLS), dan multiplexing dalam satu protokol yang dioptimasi untuk web modern.
Mengapa QUIC? Masalah TCP yang Dipecahkan
| Masalah TCP | Solusi QUIC |
|---|---|
| Head-of-Line blocking | Independent streams β stream A yang lambat tidak blokir stream B |
| TCP + TLS = 2 RTT | Handshake terintegrasi: 1-RTT (atau 0-RTT untuk reconnection) |
| Connection tied to IP | Connection ID β koneksi tetap hidup saat IP berubah (WiFiβLTE) |
| OS kernel update lambat | Implemented di user-space, mudah di-update |
| TCP congestion control kaku | Congestion control yang lebih modern (BBR, CUBIC) |
QUIC Connection Establishment
# === TCP + TLS 1.2: 3 RTT === Client Server |--- SYN ----------------------->| RTT 1: TCP handshake |<-- SYN-ACK | |--- ACK + ClientHello --------->| RTT 2: TLS handshake |<-- ServerHello + Cert | |--- Finished + App Data ------->| RTT 3: Selesai |<-- App Data | # === TCP + TLS 1.3: 2 RTT === Client Server |--- SYN ----------------------->| RTT 1: TCP + TLS |<-- SYN-ACK + ServerHello | |--- ACK + Finished + App Data ->| RTT 2: Selesai |<-- App Data | # === QUIC: 1-RTT (koneksi baru) === Client Server |--- Initial (ClientHello) ----->| RTT 1: QUIC + TLS terintegrasi |<-- Handshake (ServerHello) | |--- 0-RTT App Data ----------->| (bisa kirim data segera!) # === QUIC: 0-RTT (koneksi ulang) === Client Server |--- Initial + 0-RTT Data ----->| β Data app dikirim SEKARANG |<-- Handshake + App Data | β Response dalam 0 RTT!
QUIC Packet Structure
# QUIC Long Header Packet: # # 0 1 2 3 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 # +-+-+-+-+-+-+-+-+ # |1|1|T|T|X|X|X|X| Header Form + Type + Reserved # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Version (32) | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | DCID Len (8) | Destination Connection ID (0..160) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | SCID Len (8) | Source Connection ID (0..160) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # QUIC Short Header Packet (1-RTT): # # +-+-+-+-+-+-+-+-+ # |0|1|S|R|R|K|P|P| # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Destination Connection ID (0..160) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Packet Number (1-4 bytes) ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ # | Protected Payload ... # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
7. HTTP/3 di Atas QUIC
HTTP/3 (RFC 9114, 2022) adalah evolusi terbaru dari protokol web yang berjalan di atas QUIC. HTTP/3 mempertahankan semua fitur HTTP/2 (multiplexing, header compression, flow control) tetapi memanfaatkan keunggulan QUIC untuk menghilangkan TCP head-of-line blocking.
Perbandingan HTTP/2 vs HTTP/3
| Aspek | HTTP/2 | HTTP/3 |
|---|---|---|
| Transport | TCP + TLS | QUIC (UDP) |
| Head-of-Line | Stream-level OK, connection-level blocking di TCP | Tidak ada HoL blocking |
| Handshake | 2 RTT (TCP+TLS) / 1 RTT (TLS 1.3) | 1-RTT baru / 0-RTT resumption |
| Header Compression | HPACK | QPACK |
| Connection Migration | Tidak (tied ke IP:port) | Ya (Connection ID) |
| Encryption | Optional (TLS di layer terpisah) | Wajib (built-in) |
| Load Balancing | Standard TCP load balancer | Perlu QUIC-aware LB |
Mengapa QPACK, Bukan HPACK?
HPACK bergantung pada ordered delivery β header yang dikirim harus diterima dalam urutan yang sama untuk menjaga dynamic table sinkron. Dengan QUIC yang mendukung independent stream delivery, HPACK tidak bisa langsung digunakan. QPACK (RFC 9204) menyelesaikan ini dengan memisahkan encoding dan decoding stream.
# QPACK Architecture: # Encoder Stream (client β server) # β Mengirim dynamic table insertions # β Mengirim header acknowledgments # Decoder Stream (server β client) # β Mengirim table size updates # β Mengirim insert count increments # Request/Response Stream # β Menggunakan encoded header blocks # β Bisa reference static + dynamic table # β Bisa deferred: tunggu table update jika perlu # Contoh QPACK encoded header: # Required Insert Count: 5 # Base: 3 # Encoded Fields: # Literal with Name Reference (static index 1 β :authority) # Indexed (dynamic index 64 β content-type: application/json) # Literal with Name Reference (static index 26 β content-length)
Connection Migration
# Connection Migration β salah satu fitur terbaik HTTP/3 + QUIC # # Ketika device berpindah jaringan (WiFi β cellular), IP berubah. # Dengan TCP: koneksi terputus, harus reconnect + re-handshake. # Dengan QUIC: koneksi tetap hidup via Connection ID. Device (WiFi) Server | | |--- [CID=abc123] ----->| β Normal traffic via WiFi |<-- [CID=abc123] ------| | | ... Device pindah ke LTE, IP berubah ... | | |--- [CID=abc123] ----->| β Sama CID, IP baru! |<-- [CID=abc123] ------| β Server mengenali via CID | | (tidak perlu re-handshake!)
8. Tips Optimasi Performa
Best Practices HTTP/2
Di HTTP/1.1, developer menggunakan domain sharding (assets1.example.com, assets2.example.com) untuk mengatasi limit 6 koneksi per domain. Di HTTP/2, ini kontraproduktif β setiap domain butuh koneksi terpisah, menghilangkan manfaat multiplexing dan connection reuse. Gunakan satu domain saja!
Optimasi Resource Loading
<!-- β HTTP/1.1 style: domain sharding (JANGAN di HTTP/2) --> <link rel="stylesheet" href="https://cdn1.example.com/style.css"> <script src="https://cdn2.example.com/app.js"></script> <!-- β HTTP/2+ style: single connection, resource hints --> <link rel="preconnect" href="https://cdn.example.com"> <link rel="preload" href="/fonts/inter.woff2" as="font" crossorigin> <link rel="preload" href="/css/critical.css" as="style"> <link rel="preload" href="/js/app.js" as="script"> <!-- β 103 Early Hints (modern alternative to server push) --> <!-- Server mengirim header: --> <!-- HTTP/1.1 103 Early Hints --> <!-- Link: </css/style.css>; rel=preload; as=style --> <!-- Link: </js/app.js>; rel=preload; as=script --> <!-- β Tekan ukuran header untuk performa --> <!-- Cookie kecil, hapus header yang tidak perlu --> <!-- Gunakan HPACK-aware CDN -->
Mengecek HTTP Versi yang Digunakan
# Cek HTTP version menggunakan curl curl -sI --http2 https://www.google.com 2>/dev/null | head -5 # HTTP/2 200 # content-type: text/html; charset=ISO-8859-1 curl -sI --http3 https://www.google.com 2>/dev/null | head -5 # HTTP/3 200 # content-type: text/html; charset=ISO-8859-1 # Cek dengan verbose curl -v --http2 https://example.com 2>&1 | grep -i "ALPN\|HTTP/" # * ALPN: server accepted h2 # < HTTP/2 200 # Di browser: buka DevTools β Network β klik kanan kolom # β aktifkan "Protocol" column # Tampilkan: h2, h3, http/1.1 # Menggunakan httpstat untuk analisis lebih detail go install github.com/davecheney/httpstat@latest httpstat https://www.google.com