EP.07網路系列

HTTP/3 與 QUIC:下一代網路協定完全解析
為什麼 HTTP/3 比 HTTP/2 快?

QUIC 協定原理、0-RTT 連線、多路複用解決 Head-of-Line Blocking — 從 TCP 的致命缺陷說起,解析 HTTP/3 如何從根本上重新設計網路傳輸層, 並帶來連線遷移、0-RTT 等革命性特性。

Joseph Chen 2026 16 min read HTTP/3 · QUIC · UDP · 0-RTT

從 HTTP/1.1 到 HTTP/3 的演進史

HTTP 是網際網路的基礎協定,每一次版本升級都是為了解決上一個版本的根本限制。 理解這段演進史,才能真正看懂 HTTP/3 做了什麼、解決了什麼、又帶來了什麼新問題。

HTTP/1.1

1997
  • 每個請求需要一個 TCP 連線(1.0 是),1.1 引入 Keep-Alive 持久連線
  • Pipelining(管線化)理論上可以發多個請求,但實作上幾乎沒瀏覽器啟用
  • 瀏覽器並行開 6 個 TCP 連線來加速(Hack,不是設計)
  • Head-of-Line Blocking(HOL Blocking):同一連線的後續請求要等前面的回應完成
  • Header 不壓縮,每個請求都重複傳送大量相同的 Header(User-Agent、Cookie 等)

瓶頸:HTTP 層的 HOL Blocking + 無壓縮 Header

HTTP/2

2015
  • 多路複用(Multiplexing):一個 TCP 連線可以並行傳輸多個請求/回應(Stream)
  • Header 壓縮(HPACK):重複的 Header 只傳差異,大幅減少傳輸量
  • Server Push:伺服器主動推送資源(已被 Chrome 移除支援)
  • 二進位格式(Binary Framing):替代文字格式,解析更高效
  • 解決了 HTTP 層的 HOL Blocking,但 TCP 層的 HOL Blocking 依然存在!

瓶頸:TCP 層的 HOL Blocking(丟包時所有 Stream 都要等)

HTTP/3

2022 (RFC 9114)
  • 基於 UDP + QUIC 協定(告別 TCP,從根本解決 TCP 的限制)
  • 真正解決所有層次的 HOL Blocking(每個 Stream 獨立追蹤丟包)
  • 0-RTT 連線(第二次連線可以直接傳資料,不需要握手)
  • 連線遷移(Connection Migration):切換網路不斷線
  • TLS 1.3 內建(安全性是設計的一部分,不是附加功能)

現況:主流瀏覽器和 CDN 已全面支援,佔全球 HTTP 流量 ~30%


TCP 的致命缺陷 — Head-of-Line Blocking

HTTP/2 的多路複用解決了 HTTP 層的 HOL Blocking,但它依然建立在 TCP 之上。 TCP 保證資料按序到達,這個保證本身就是 HOL Blocking 的根源。 要理解為什麼,需要先理解 TCP 如何處理丟包。

HTTP/2 的 TCP 層 HOL Blocking(丟包場景)
HTTP/2 有三個並行的 Stream(同一個 TCP 連線):

  Stream 1 傳輸 CSS:[Packet 1][Packet 2][Packet 3]  → 資料完整
  Stream 2 傳輸 JS :[Packet A][Packet B][Packet C]  → 資料完整
  Stream 3 傳輸圖片:[Packet X]   [丟包!]   [Packet Z]  → Packet Y 丟失!

TCP 的反應:
  TCP 發現 Packet Y 遺失(收到 Packet Z 但缺少 Y,亂序)
  TCP 要求重傳 Packet Y
  在 Packet Y 重傳並到達之前,TCP 緩衝區停止往上層(HTTP/2)傳送任何資料

  → Stream 1 的 CSS 已完整,但 HTTP/2 無法取用(TCP 卡住了)
  → Stream 2 的 JS 已完整,但 HTTP/2 無法取用(TCP 卡住了)
  → 整個頁面的渲染因為一張圖片的一個封包丟失而延遲!

  這就是 TCP 層的 HOL Blocking:
  所有 HTTP/2 的 Stream 共用同一條 TCP 連線,
  TCP 一旦因丟包暫停,所有 Stream 都跟著停。

──────────────────────────────────────────────────────────

HTTP/3(QUIC)的解決方案:每個 Stream 獨立追蹤丟包

  Stream 1 傳輸 CSS:[Packet 1][Packet 2][Packet 3]  → 立刻交給應用層
  Stream 2 傳輸 JS :[Packet A][Packet B][Packet C]  → 立刻交給應用層
  Stream 3 傳輸圖片:[Packet X]   [丟包!]   [Packet Z]  → 只有 Stream 3 等待重傳

  → Stream 1 和 2 不受影響,CSS 和 JS 立刻渲染
  → 只有圖片(Stream 3)在等待,其他資源正常處理
  → 整體頁面體驗:只有圖片慢,不是整個頁面都卡住

為什麼 QUIC 能做到這一點?因為 QUIC 是在 UDP 之上自己實作了可靠傳輸, 而且每個 Stream 的流量控制和丟包重傳是完全獨立的。 TCP 則是在 OS 核心層實作,無法針對個別應用層的 Stream 做差異化處理。

HOL Blocking 在什麼網路環境最明顯?

丟包率越高,HOL Blocking 的影響越嚴重。4G/5G 行動網路的丟包率約 0.5~2%, WiFi 環境在干擾多時可達 1~3%,長途跨洋線路也有 0.5~1% 的丟包率。 在這些場景下,HTTP/3 對 HTTP/2 的效能優勢最為顯著(可達 30~50%)。 在低延遲、低丟包的同機房環境,差距相對不明顯。


QUIC 協定 — 為什麼要建構在 UDP 上?

選擇 UDP 作為 QUIC 的基礎,不是因為 UDP 更可靠,而是因為 UDP 更「空白」。 UDP 沒有連線概念、沒有流量控制、沒有可靠傳輸,這反而讓 QUIC 可以在應用層自由定義這些行為, 不受 OS 核心 TCP 實作的限制。

TCP vs UDP 的根本差異,以及 QUIC 的定位
TCP(Transmission Control Protocol):
  ✓ 可靠傳輸:保證封包送達、按序交付
  ✓ 流量控制:防止傳送速度超過接收方的處理能力
  ✓ 擁塞控制:偵測網路擁塞並自動降速
  ✗ 連線建立:三次握手(1.5 RTT 的延遲開銷)
  ✗ 協定邏輯在 OS 核心層:要更新協定就要更新 OS(極慢)
  ✗ HOL Blocking:一旦丟包,所有資料都要等

UDP(User Datagram Protocol):
  ✓ 速度快:沒有連線建立、沒有確認機制
  ✓ 協定邏輯在應用層:可以快速迭代更新
  ✗ 不保證送達:封包可能丟失
  ✗ 不保證順序:封包可能亂序到達
  ✗ 沒有流量控制和擁塞控制

QUIC = UDP + 在應用層重新實作上面 TCP 的優點 + TLS 1.3

  QUIC 的可靠傳輸:自己實作 ACK + 重傳,但以 Stream 為粒度(非連線粒度)
  QUIC 的流量控制:Stream 級別 + 連線級別,兩層控制
  QUIC 的擁塞控制:預設 Cubic(同 TCP),但可熱更新成 BBR 等演算法
  QUIC 的加密:TLS 1.3 內建,不能關閉(所有 HTTP/3 流量都是加密的)
  QUIC 的更新:協定邏輯在使用者空間,Chrome 更新就能升級協定邏輯(不需要等 OS 更新)

連線建立的延遲比較

RTT(Round-Trip Time)是封包從客戶端發出、到伺服器、再回到客戶端的時間。 連線建立所需的 RTT 越少,第一個 byte 到達的延遲就越低。

各協定連線建立流程比較
──────────────────────────────────────────────────────────
TCP + TLS 1.2(舊方式,共需 3 RTT)
──────────────────────────────────────────────────────────

  客戶端                          伺服器
    │                                │
    │ ──── SYN ──────────────────→  │  ← TCP 握手第 1 步
    │ ←─── SYN-ACK ──────────────  │  ← TCP 握手第 2 步
    │ ──── ACK ──────────────────→  │  ← TCP 握手第 3 步(1.5 RTT 完成 TCP)
    │                                │
    │ ──── ClientHello ──────────→  │  ← TLS 握手第 1 步
    │ ←─── ServerHello + Cert ───   │  ← TLS 握手第 2 步
    │ ──── Key Exchange ─────────→  │  ← TLS 握手第 3 步
    │ ←─── Finished ─────────────   │  (又 1.5 RTT 完成 TLS)
    │                                │
    │ ──── HTTP Request ─────────→  │  ← 終於可以開始傳資料
    │ ←─── HTTP Response ─────────  │
    │                                │
  總計:3 RTT 後才收到第一個 byte

──────────────────────────────────────────────────────────
TCP + TLS 1.3(改進版,共需 2 RTT)
──────────────────────────────────────────────────────────

  TLS 1.3 把握手合併到 2 步:
  客戶端 → 伺服器:ClientHello(含 Key Share)
  伺服器 → 客戶端:ServerHello + Certificate + Finished(含加密資料)
  但 TCP 握手的 1.5 RTT 依然無法消除

  總計:2.5 RTT(TCP 1.5 RTT + TLS 1.3 握手 1 RTT)

──────────────────────────────────────────────────────────
QUIC 初次連線(1-RTT)
──────────────────────────────────────────────────────────

  QUIC 把 TCP 握手和 TLS 握手合併成一個步驟!

  客戶端                          伺服器
    │                                │
    │ ── QUIC Initial + ClientHello → │  ← QUIC + TLS 握手合併,一次發送
    │ ← QUIC + ServerHello + Data ── │  ← 伺服器回應同時包含加密資料
    │                                │
    │ 之後的請求可以直接傳送           │
    │                                │
  總計:1 RTT 後開始收到有效資料

──────────────────────────────────────────────────────────
QUIC 0-RTT(再次連線,0 RTT)
──────────────────────────────────────────────────────────

  你曾經連線過這個伺服器(例如昨天訪問過這個網站)
  QUIC 把上一次的 Session Ticket 儲存在瀏覽器中

  客戶端                          伺服器
    │                                │
    │ ── 0-RTT Data(帶 Session Ticket)→ │  ← 直接傳資料,不需要握手!
    │ ←── Response ──────────────── │
    │                                │
  總計:0 RTT → 第一個請求的延遲接近於 0!

0-RTT 的安全注意事項

0-RTT 有一個安全限制:它容易受到「重放攻擊」(Replay Attack)。 攻擊者可以截取 0-RTT 的資料並重新發送。因此,0-RTT 只適合「冪等請求」(Idempotent): GET 請求(讀取資料)是安全的,但 POST 付款、POST 下單這類有副作用的請求不應使用 0-RTT。 瀏覽器和 QUIC 實作通常會自動處理這個限制。


連線遷移(Connection Migration)

這是 HTTP/3 最被低估的特性之一。在行動設備普及的今天, 用戶從 WiFi 切換到行動網路是日常操作,但 TCP 連線在這種情況下會直接斷開。 QUIC 用一個優雅的設計完全解決了這個問題。

TCP 連線 vs QUIC 連線的識別方式
──────────────────────────────────────────────────────────
TCP 連線的識別:四元組(4-tuple)
──────────────────────────────────────────────────────────

  一條 TCP 連線由這四個元素唯一識別:
  (Source IP, Source Port, Destination IP, Destination Port)

  你在咖啡廳連 WiFi,IP = 192.168.1.100
  正在下載一個大檔案,連線識別:
  (192.168.1.100, 54321, 93.184.216.34, 443)

  你切換到手機行動網路,IP 變成 10.0.0.50
  TCP 連線識別變成:
  (10.0.0.50, 54321, 93.184.216.34, 443)  ← 完全是另一條連線!

  → TCP 根本不知道這是「同一個用戶換了網路」
  → 原本的 TCP 連線被丟棄(Timeout 或 RST)
  → 需要重新建立 TCP + TLS 握手(2~3 RTT)
  → 正在下載的資料中斷,需要重新開始或靠應用層斷點續傳

──────────────────────────────────────────────────────────
QUIC 連線的識別:Connection ID(64-bit 隨機值)
──────────────────────────────────────────────────────────

  一條 QUIC 連線由一個隨機產生的 64-bit Connection ID 識別
  這個 ID 由客戶端選擇,與 IP 和 Port 完全無關

  你在咖啡廳連 WiFi,QUIC Connection ID = 0xABCDEF1234567890
  你切換到手機行動網路,IP 改變了,但 Connection ID 不變!

  QUIC 的流程:
  1. 客戶端偵測到 IP 改變(Source IP 從 WiFi 地址變成行動網路地址)
  2. 客戶端繼續使用相同的 Connection ID 發送資料
  3. 伺服器收到來自新 IP 的封包,但 Connection ID 相同
  4. 伺服器認可這是連線遷移,更新對應的 IP 記錄
  5. 連線繼續,幾乎沒有中斷!

  → 用戶體驗:短暫的傳輸停頓(幾十毫秒),然後自動恢復
  → 不需要重新握手,不需要重新傳資料

HTTP/2 + WiFi → 4G

  • ×TCP 連線因 IP 改變而斷開
  • ×需要重新 TCP + TLS 握手(~200ms RTT × 2)
  • ×正在上傳/下載的資料中斷
  • ×視訊通話斷線,需要重新撥打
  • ×遊戲中斷線,需要重新登入

HTTP/3 + WiFi → 4G

  • Connection ID 不變,連線識別不中斷
  • 短暫停頓(幾十毫秒)後自動恢復
  • 檔案傳輸無縫繼續
  • 視訊通話幾乎感覺不到切換
  • 遊戲角色可能有短暫延遲,但不斷線

連線遷移對行動使用者的影響是真實且可測量的。 在捷運、公車等移動場景中,用戶頻繁切換基地台和 WiFi 熱點, HTTP/3 的連線遷移讓這些切換過程對應用層完全透明,使用者幾乎感覺不到網路的切換。


在實際專案中支援 HTTP/3

啟用 HTTP/3 有幾種難度不同的方式,從一鍵啟用(Cloudflare)到自行編譯 Nginx。 在動手之前,先確認你的網站是否已經支援 HTTP/3,以及如何驗證。

驗證網站是否支援 HTTP/3
# 方法一:curl(需要 curl 7.66+ 且編譯時包含 QUIC 支援)
curl -I --http3 https://www.google.com
# 成功時 HTTP 版本欄位會顯示 HTTP/3

# 查看 curl 是否有 QUIC 支援
curl --version | grep -i quic
# 應看到類似:Features: ... HTTP3 ...

# 方法二:Chrome DevTools(最常用的方式)
# 1. 打開 Chrome,前往目標網站
# 2. 按 F12 → Network tab
# 3. 找到 Protocol 欄位(若沒有,右鍵欄位標題 → 勾選 Protocol)
# 4. 看到 "h3" 就是 HTTP/3,"h2" 是 HTTP/2

# 方法三:使用 curl 查看 Alt-Svc 回應 Header
curl -sI https://www.cloudflare.com | grep -i alt-svc
# 回應範例:
# alt-svc: h3=":443"; ma=86400
# 這表示伺服器支援 HTTP/3,且下次連線可以直接用(ma=86400 是快取時間)

# 方法四:線上工具
# https://http3check.net → 輸入你的網域,直接測試
# https://www.http3check.net

# 方法五:Chrome 的 net-internals
# chrome://net-internals/#quic → 查看當前所有 QUIC 連線狀態
Nginx 配置(啟用 HTTP/3 / QUIC)
# 前置需求:
# Nginx 1.25.0+ 已內建 QUIC 支援(不需要額外 patch)
# 或使用 nginx-quic 分支(1.25.0 之前)

# /etc/nginx/nginx.conf
events {
  worker_connections 1024;
}

http {
  server {
    # 同時監聽 TCP(HTTP/1.1 和 HTTP/2)和 QUIC(HTTP/3)
    listen 443 ssl;             # TCP + TLS(HTTP/1.1 和 HTTP/2 的 fallback)
    listen 443 quic reuseport;  # UDP + QUIC(HTTP/3)
    # reuseport 讓多個 worker process 共享同一個 UDP Port,提高吞吐量

    server_name example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    # HTTP/3 強制要求 TLS 1.3(QUIC 內建 TLS 1.3,無法降級)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    # 關鍵 Header:告訴瀏覽器「這台伺服器支援 HTTP/3」
    # 瀏覽器下一次訪問會優先嘗試 HTTP/3
    # ma=86400 表示這個資訊快取 86400 秒(1天)
    add_header Alt-Svc 'h3=":443"; ma=86400';

    # 啟用 HTTP/2(同一個伺服器提供 HTTP/2 fallback)
    http2 on;

    location / {
      proxy_pass http://backend;

      # 傳遞真實 IP(QUIC 連線遷移時 IP 可能改變,記錄 Connection ID 更準確)
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
    }
  }

  # HTTP → HTTPS 強制跳轉
  server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
  }
}
Cloudflare 的 HTTP/3(最簡單,一鍵啟用)
對於已使用 Cloudflare 作為 CDN 的網站,啟用 HTTP/3 只需要:

  1. 登入 Cloudflare Dashboard(dash.cloudflare.com)
  2. 選擇你的 Domain
  3. 前往 Speed → Optimization → Protocol Optimization
  4. 找到「HTTP/3 (with QUIC)」→ 開啟

就這樣!所有流量自動升級,不需要在伺服器端做任何改動。

Cloudflare 會在它的邊緣節點(Edge)處理 HTTP/3,
然後用 HTTP/1.1 或 HTTP/2 和你的 Origin Server 通訊。
(稱為「HTTP/3 Termination at Edge」)

這是最推薦的方式,因為:
  ✓ 不需要維護自己的 QUIC 實作
  ✓ Cloudflare 的 QUIC 實作持續更新和優化
  ✓ 自動處理瀏覽器 fallback(不支援 HTTP/3 的客戶端自動降級)
  ✓ 沒有額外費用(免費方案也包含)

其他 CDN 的支援:
  AWS CloudFront:支援 HTTP/3(2022 年開始)
  Fastly        :支援 HTTP/3
  Vercel        :自動支援 HTTP/3(無需配置)
  Netlify       :自動支援 HTTP/3(無需配置)
Next.js 專案確認 HTTP/3 支援(Vercel 部署)
// Next.js 部署到 Vercel 時,HTTP/3 自動啟用,不需要任何配置

// 你可以用以下方式在 API Route 中記錄協定資訊
// app/api/debug/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  return NextResponse.json({
    // Vercel 會在請求 Header 中注入 HTTP 版本資訊
    protocol: request.headers.get('x-forwarded-proto'),
    // 真實連線資訊
    via: request.headers.get('via'),
    // Cloudflare 用戶可以看這個
    cfRay: request.headers.get('cf-ray'),
    // 連線使用的 TLS 版本
    cfTlsVersion: request.headers.get('cf-tls-version'),
  });
}

// 在前端取得協定資訊(瀏覽器端)
// 注意:瀏覽器沒有直接 API 取得 HTTP 版本
// 需要用 Chrome DevTools 的 Network tab 觀察 Protocol 欄位
// 或使用 Performance API 觀察 nextHopProtocol

const entries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[];
if (entries.length > 0) {
  console.log('Protocol:', entries[0].nextHopProtocol);
  // 'h3' = HTTP/3, 'h2' = HTTP/2, 'http/1.1' = HTTP/1.1
}

HTTP/3 效能數據與局限性

HTTP/3 在實測中確實帶來了可量化的效能提升,但效益因網路環境不同而差異很大。 這裡提供真實的數據和誠實的局限性分析,幫助你判斷是否值得在你的情境中啟用。

實測效能對比

3G 網路(高延遲、高丟包)

HTTP/1.14.5 秒
HTTP/23.2 秒
HTTP/32.1 秒

HTTP/3 比 HTTP/2 快 34%

WiFi(低延遲、低丟包)

HTTP/1.11.2 秒
HTTP/20.9 秒
HTTP/30.85 秒

WiFi 環境差距較小(5~10%)

重複訪問(0-RTT 效果)

HTTP/2(首次)200ms 握手
HTTP/3(首次)100ms 握手
HTTP/3(重連)~0ms 握手

0-RTT 使重複訪問延遲幾乎為零

HTTP/3 的局限性(誠實面對)

HTTP/3 不是萬能的——這些場景要注意
──────────────────────────────────────────────────────────
問題 1:UDP 被企業防火牆封鎖
──────────────────────────────────────────────────────────

  很多企業網路、VPN、政府機構的防火牆預設封鎖 UDP 443 Port
  QUIC 跑在 UDP 上,所以在這些環境中 HTTP/3 完全無法使用

  不用擔心:瀏覽器會自動 Fallback
  瀏覽器嘗試 HTTP/3 失敗後,會自動降級到 HTTP/2(TCP)
  對使用者透明,只是少了 HTTP/3 的效能優勢

  影響範圍:部分企業用戶(估計 5~10%)

──────────────────────────────────────────────────────────
問題 2:Server 端支援需要特殊版本或配置
──────────────────────────────────────────────────────────

  Nginx:需要 1.25.0+(有 QUIC 內建支援)或 nginx-quic 分支
  Apache:透過 mod_quic,支援程度不如 Nginx 成熟
  老舊的 Load Balancer / WAF 可能不支援 QUIC

  自建伺服器的挑戰:
  ✗ 並非所有主機商都提供支援 QUIC 的 Nginx 版本
  ✗ QUIC 的 UDP 需要特殊的 kernel 調優(socket buffer size)

──────────────────────────────────────────────────────────
問題 3:低延遲環境效益不明顯
──────────────────────────────────────────────────────────

  HTTP/3 的優勢主要來自:
  1. 減少握手 RTT(0-RTT)→ 初始延遲越高,效益越大
  2. 解決 HOL Blocking → 丟包率越高,效益越大
  3. 連線遷移 → 行動網路場景效益大

  反過來說,在以下場景效益不明顯:
  ✗ 同機房的微服務通訊(延遲 < 1ms,丟包率接近 0)
  ✗ 已有優化的有線網路辦公環境
  ✗ 短小的 API 請求(資料量小,效能瓶頸不在傳輸上)

──────────────────────────────────────────────────────────
問題 4:CPU 消耗略高
──────────────────────────────────────────────────────────

  QUIC 的加密、封包分割、ACK 處理都在應用層(User Space)
  TCP 的這些操作由 OS 核心處理,可以用 GSO / GRO 等硬體加速

  結果:QUIC 的 CPU 消耗比 TCP 高約 10~20%
  對於 CPU 已是瓶頸的伺服器,啟用 HTTP/3 可能反而帶來負面影響

何時值得啟用 HTTP/3?

值得啟用的場景

  • 服務全球用戶或以行動裝置為主的使用者(延遲高)
  • 有大量 CSS/JS/圖片資源的前端網站(多路複用效益大)
  • 已使用 Cloudflare / Vercel / Netlify(一鍵啟用,零成本)
  • SaaS 產品需要優化用戶首次載入體驗
  • 遊戲或即時通訊應用(連線遷移特性有價值)

效益有限的場景

  • 純後端微服務間的 API 通訊(用 gRPC/HTTP2 更合適)
  • 受嚴格企業防火牆管控的 Intranet 系統
  • API Gateway 到 Origin Server 的內部通訊(低延遲環境)
  • CPU 已是伺服器瓶頸的場景(QUIC 的 User Space 處理消耗更多 CPU)
  • 主要用戶在公司辦公室使用有線網路(穩定低丟包)

HTTP/3 的普及現況(2026)

根據 W3Techs 的數據,HTTP/3 已佔全球 HTTP 流量的約 30%。 Google、Facebook、Cloudflare、YouTube 都已大規模部署。 主流瀏覽器(Chrome 87+、Firefox 88+、Safari 14+、Edge 90+)全面支援。 對於新專案,如果你用 Vercel 或 Cloudflare,HTTP/3 是免費自動啟用的, 沒有理由不用。對於自建伺服器,優先考慮 Nginx 1.25+ 或使用 CDN 終止 QUIC。


Tags

HTTP/3QUICUDP0-RTTHead-of-Line BlockingConnection MigrationTLS 1.3Nginx

上一篇

EP.06 gRPC 與 Protocol Buffers

下一篇

EP.08(Coming Soon)