Sau bài này bạn làm được: giải thích tại sao HTTPS tốn thêm thời gian so với HTTP; biết HTTP/2 multiplexing giúp gì và còn hạn chế gì; đọc log LB có HTTP/2.0 hay HTTP/3 và biết hệ quả vận hành.
HTTP/1.1 — nền tảng, nhưng có điểm yếu
HTTP/1.1 dùng TCP làm transport và mặc định bật keep-alive — tái sử dụng cùng một TCP connection cho nhiều request thay vì đóng sau mỗi request.
[HTTP/1.0 - mỗi request mở TCP mới]
Client: TCP open → GET /a → response → TCP close
Client: TCP open → GET /b → response → TCP close ← tốn RTT mỗi lần
[HTTP/1.1 với keep-alive]
Client: TCP open → GET /a → response
→ GET /b → response
→ GET /c → response → TCP close (khi idle timeout)
Vấn đề còn lại: head-of-line blocking HTTP
Trong HTTP/1.1, các request xếp hàng trên một connection. Request B không thể nhận response cho đến khi request A hoàn thành. Trình duyệt thường mở 6 TCP connection song song tới một origin để lách giới hạn này.
HTTPS = HTTP + TLS (không phải giao thức mới)
HTTPS không thay thế HTTP — nó đặt HTTP bên trong một TLS tunnel:
[HTTPS stack trên server]
Application (NGINX, Node, app):
HTTP ─▶ TLS ─▶ TCP ─▶ IP ─▶ Ethernet
Thứ tự handshake khi kết nối mới:
1. TCP handshake (1 RTT)
2. TLS handshake (TLS 1.2: 2 RTT | TLS 1.3: 1 RTT)
3. HTTP request (1 RTT)
─────────────────────────
Tổng tối thiểu: 3–4 RTT trước byte đầu tiên (HTTP/1.1, TLS 1.2)
TLS 1.3 giảm handshake xuống 1 RTT (và 0-RTT resumption cho session cũ). Đây là lý do nên ưu tiên TLS 1.3 trên server mới.
Reverse proxy terminate TLS:
Internet → [LB / NGINX: terminate TLS] → HTTP nội bộ → [App container]
hoặc
→ HTTPS re-encrypt → [App container]
Log access của NGINX sẽ ghi request HTTP (sau khi terminate), không còn TLS. Đây là lý do “tại sao log không thấy HTTPS”.
HTTP/2 — một TCP, nhiều stream song song
HTTP/2 giữ TCP làm transport nhưng giới thiệu binary framing và multiplexing:
[HTTP/1.1: nhiều request = nhiều connection]
Conn1: ──── GET /style.css ──────────────────────────▶
Conn2: ──── GET /script.js ──────────────────────────▶
Conn3: ──── GET /image.png ──────────────────────────▶
[HTTP/2: nhiều stream trên 1 TCP connection]
Conn1: Stream1: GET /style.css ──▶ [dữ liệu xen kẽ]
Stream2: GET /script.js ──▶
Stream3: GET /image.png ──▶
Lợi ích:
- Giảm số TCP handshake và TLS handshake.
- Header compression (HPACK) tiết kiệm bandwidth.
- Stream priority và flow control chi tiết hơn HTTP/1.1.
HTTP/2 Server Push đã chết. Chrome gỡ từ 106 (2022), Firefox cũng đã tắt; Fastly và nhiều CDN bỏ hỗ trợ. Lý do: push không biết client đã cache chưa → thường lãng phí bandwidth. Thay thế:
103 Early HintsvớiLink: rel=preload— server báo sớm resource cần tải mà không push dữ liệu.
Hạn chế còn lại: TCP head-of-line blocking
Nếu một TCP packet bị mất, toàn bộ stream trên connection đó phải chờ retransmit, dù nhiều stream không liên quan đến packet đó. Đây là điểm mà HTTP/3 xử lý.
CVE-2023-44487 — HTTP/2 Rapid Reset
Tháng 10/2023 Cloudflare, Google, AWS công bố lỗ hổng Rapid Reset: attacker mở stream rồi gửi RST_STREAM ngay lập tức, vòng lặp triệu lần/s → server phát sinh tải CPU để xử lý trạng thái stream mà attacker đã huỷ → DDoS layer-7 kỷ lục (hơn 398 triệu RPS tại Google).
Cách giảm thiểu (bắt buộc cập nhật):
- Nâng phiên bản: NGINX ≥ 1.25.3, Envoy ≥ 1.27.3, Go ≥ 1.21.3, Node.js ≥ 18.18.2, HAProxy ≥ 2.8.3, Kestrel (.NET) đã patch.
- Giới hạn số stream đồng thời (
http2_max_concurrent_streamsmặc định giảm, ~100). - Theo dõi metric
RST_STREAM ratebất thường; WAF/CDN (Cloudflare, AWS Shield) đã có rule mặc định.
Đây là lý do 2025 mọi reverse proxy chạy production phải audit patch-level HTTP/2.
HTTP/3 và QUIC — transport mới
HTTP/3 thay TCP bằng QUIC (thường chạy trên UDP):
HTTP/3 stack:
HTTP/3 ─▶ QUIC ─▶ UDP ─▶ IP ─▶ Ethernet
QUIC tự cài đặt:
- Multiplexing stream (độc lập nhau, mất gói chỉ ảnh hưởng stream đó)
- Encryption tích hợp (TLS 1.3 embedded)
- Connection migration (đổi IP/port mà không mất kết nối)
Góc vận hành:
- Cần firewall/SG mở UDP (cổng 443) — nhiều môi trường corporate chặn UDP.
- CDN (Cloudflare, GCP Cloud Load Balancing, CloudFront, Fastly) đã bật HTTP/3 mặc định tại edge; app backend thường vẫn HTTP/1.1 hoặc HTTP/2 nội bộ.
- Client tự fallback về HTTP/2 nếu QUIC không thông — biết qua header
Alt-Svc: h3=":443".
ALPN — chọn phiên bản trong TLS handshake
Client và server thoả thuận phiên bản HTTP trong TLS ClientHello bằng extension ALPN (Application-Layer Protocol Negotiation):
ClientHello ALPN: ["h2", "http/1.1"] ← client hỗ trợ HTTP/2 và HTTP/1.1
ServerHello ALPN: "h2" ← server chọn HTTP/2
HTTP/3: ALPN "h3" — nhưng ký qua QUIC chứ không qua TLS trên TCP.
Khi debug curl --http2 -v ... thấy dòng ALPN: offers h2, ALPN: server accepted h2 là tốt. Nếu server chỉ hỗ trợ http/1.1 thì client HTTP/2 sẽ downgrade âm thầm — đôi khi là nguồn của performance regression bất ngờ sau khi đổi LB.
0-RTT và rủi ro replay
QUIC (và TLS 1.3) hỗ trợ 0-RTT: client gửi request kèm luôn dữ liệu ngay trong ClientHello khi đã có session cached → tiết kiệm 1 RTT.
Rủi ro: attacker chặn được gói 0-RTT có thể replay nhiều lần — server không cách nào phân biệt. Quy tắc:
- Chỉ cho phép idempotent request (GET, HEAD) qua 0-RTT.
- Application phải anti-replay cho mutation (hoặc disable 0-RTT cho POST/PUT/DELETE).
- NGINX:
ssl_early_data onphải đi kèm headerEarly-Data: 1để app biết và xử lý an toàn.
Timeout trong LB và proxy — điểm hay cấu hình sai
Client ──▶ [LB] ──▶ [App]
Các loại timeout cần khớp nhau:
LB idle timeout: thời gian không có dữ liệu → đóng kết nối
LB request timeout: thời gian từ request đến response đầy đủ
App keepalive timeout:thời gian server giữ connection TCP mở
Lỗi điển hình:
App timeout = 60s | LB timeout = 30s
→ LB đóng connection sau 30s
→ App vẫn đang xử lý → client nhận 502/504
Quy tắc: LB timeout ≥ app worst-case response time + buffer.
Tóm tắt
- HTTPS = HTTP + TLS — cùng giao thức, thêm mã hóa và xác thực.
- HTTP/2 giải quyết head-of-line ở tầng HTTP, nhưng TCP vẫn là điểm yếu.
- HTTP/3/QUIC thay TCP bằng UDP+QUIC, giải quyết head-of-line thực sự.
- Timeout giữa LB và app phải được cấu hình đồng bộ — không khớp là nguồn gốc 502/504 bí ẩn.
Câu hỏi hay gặp
HTTPS + TLS 1.2: khoảng bao nhiêu RTT trước khi có response?
Trả lời: TCP ~1 RTT (handshake) + TLS 1.2 thường ~2 RTT (full handshake) trước khi gửi HTTP request; sau đó còn thời gian xử lý app. Tổng tối thiểu vài RTT trước byte đầu tiên của response (chưa kể DNS nếu chưa cache).
HTTP/2 có hết head-of-line blocking hoàn toàn không?
Trả lời: Hết ở tầng HTTP (nhiều stream trên một connection), nhưng TCP vẫn HOL nếu mất gói — mọi stream trên cùng connection có thể bị chậm theo. HTTP/3/QUIC giảm vấn đề này ở tầng transport.
502 khi tác vụ dài ~45s, app log sạch — kiểm tra gì trước?
Trả lời: Timeout phía proxy/LB/Ingress nhỏ hơn thời gian xử lý (idle/request timeout). So sánh cấu hình NGINX/Ingress/ALB với timeout ứng dụng và upstream.
Bài tiếp theo (Giai đoạn II): TLS — chứng chỉ, chuỗi CA và SNI — đi sâu vào cơ chế trust và lý do ERR_CERT_* xuất hiện.