HTTP
HTTP Overview
HTTP 是運作在應用層的常見協議,一開始是專門為傳輸 HTML 所設計的,後來隨著網路的發展,逐漸演變成一個通用的資料傳輸協定。
HTTP 中有幾個重要的觀念,包括 :
- stateless: 每個請求都是獨立的,伺服器不會記住之前的請求狀態,如果需要維持狀態,必須透過 Cookie 來實現
- request-response model: 客戶端發送請求,伺服器處理後返回回應
一個基本的 HTTP 請求如下 :
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 45
User-Agent: Mozilla/5.0
Accept: application/json
{"name":"Alice","email":"alice@example.com"}
HTTP/1.1 200 OK
Date: Tue, 05 Nov 2025 10:00:00 GMT
Server: nginx/1.20.1
Content-Type: application/json
Content-Length: 58
{"id":123,"name":"Alice","email":"alice@example.com"}
HTTP Method
HTTP method 定義了客戶端對資源的操作類型,每個方法都有特定的語義和期望的行為模式。
- GET: 用於請求資源
- POST: 用於提交資料以創建資源
- PUT: 用於更新整個資源
- DELETE: 用於刪除資源
- PATCH: 用於更新部分資源
- HEAD: 與 GET 類似,但只返回標頭,不包含主體
- OPTIONS: 用於預檢或查詢資源支援的 HTTP 方法
HTTP Status Code
HTTP status code 是伺服器告訴客戶端請求處理結果的標準方式,總共可以分成五種。
1xx 表示臨時回應,用於通知客戶端請求已被接收且正在處理中,較為少見。
- 100 Continue: 如果有人想要上傳 10GB 的檔案,他可以先發一個帶有
Expect: 100-continue標頭的請求,詢問伺服器是否準備好接收這個大檔案,避免浪費時間和頻寬 - 101 Switching Protocols: 用於升級協議,例如從 HTTP/1.1 升級到 WebSocket
2xx 表示請求已成功被伺服器接收並處理。
- 200 OK: 最常見的狀態碼,幾乎所有成功的請求都會返回這個狀態碼
- 201 Created: 通常用於 POST 創建資源的時候
- 202 Accepted: 常用於非同步處理場景,表示請求已被接受,但還沒完成
- 204 No Content: 常用於 DELETE,表示資源已被刪除,且不需要返回任何內容
3xx 表示客戶端需要採取額外的動作來完成請求,通常是訪問不同的網址。
- 301 Moved Permanently: 表示資源已永久移動到新位置
- 302 Found: 表示資源暫時在不同位置
- 304 Not Modified: 常跟快取搭配使用,例如下載網頁,如果資源沒變,伺服器就回 304,讓瀏覽器用快取
4xx 表示錯誤出在客戶端,像是請求語法錯誤、未授權、資源不存在等。
- 400 Bad Request: 通常是請求少了必要參數或格式錯誤
- 401 Unauthorized: 表示需要身份驗證,通常是缺少或無效的憑證
- 403 Forbidden: 通常是表示有登入但沒有權限存取這個資源
- 404 Not Found: 表示請求的資源不存在
- 429 Too Many Requests: 通常是被 rate limiter 擋下來,表示短時間內請求次數過多
5xx 表示伺服器出了問題,無法完成請求。
- 500 Internal Server Error: 一個通用的伺服器錯誤,有可能是程式碼錯誤或是遇到意外狀況
- 502 Bad Gateway: 通常是 reverse proxy 或 gateway 後面的上游伺服器有問題,導致無法回應有效的結果
- 503 Service Unavailable: 表示伺服器暫時無法處理請求,可能是網路過載或是正在維護
- 504 Gateway Timeout: 表示上游伺服器處理時間太長
HTTP Version
HTTP/0.9
HTTP/0.9 是 HTTP 協定的第一個版本,於 1991 年由 Tim Berners-Lee 發布。
這個版本非常簡單,只支援最基本的功能,主要用於傳輸純文字的 HTML 檔案。
HTTP/1.0
隨著 Web 的快速發展,HTTP/0.9 很快就暴露出許多限制,像是無法支援圖片或表單等等,為了解決這些問題,1996 年發布了 HTTP/1.0。
HTTP/1.0 引入了許多新特性,包括 :
- Header: 可以在請求和回應中傳遞額外的資訊,例如 Content-Type、Content-Length、User-Agent
- Status Code: 定義了標準的狀態碼來表示請求的結果,例如 200 OK、404 Not Found
- Content-Type: 允許伺服器告訴客戶端回應的內容類型,例如 text/html、image/png
- Cache: 引入了
Expires標頭,讓客戶端可以快取資源,減少重複請求
HTTP/1.1
雖然 HTTP/1.0 支援了許多新功能,但它依舊沿用了 HTTP/0.9 的老舊連線模型,每個請求都需要建立一個新的 TCP 連接,這導致了嚴重的效能瓶頸。
為了解決這些問題,1999 年發布了 HTTP/1.1,這個版本引入了許多改進效能的功能,包括 :
- Persistent Connection: 預設開啟
Connection: keep-alive,允許在同一個 TCP 連接上處理多個請求 - Pipelining: 允許客戶端在收到第一個請求的回應之前,連續發送多個請求
- Cache Control: 引入了現代的快取控制標頭
Cache-Control - Method: 新增了
OPTIONS、HEAD、PUT、DELETE等方法 - Host Header: 允許發送請求到同一台伺服器上的不同網站,支援虛擬主機
- Chunked Transfer Encoding: 允許伺服器邊生成邊傳輸內容,不需要預先知道內容長度
HTTP/1.1 的這些改進大幅提升了效能,至今仍然是最主流的 HTTP 版本。
HTTP/2
前面提到了 HTTP/1.1 有了 pipelining 可以一次發送多個請求,但實際上由於伺服器仍然需要按照請求順序回應,這導致了如果第一個請求很慢,後面所有請求都必須等待,這種現象稱為 Head-of-Line Blocking。
並且由於這個功能實作起來很複雜,導致很多瀏覽器並沒有真正支援這個功能,可以參考 stackoverflow 的討論。
也因此,Google 在 2015 年主導了 HTTP/2 的標準化,並在 RFC 7540 中發布,HTTP/2 引入了許多新的特性來解決 HTTP/1.1 的效能瓶頸問題。
在 HTTP/2 中,它重新定義了整個協議,分成以下幾個重要的部分 :
- Connection: TCP 連接
- Stream: 多個獨立的雙向資料流,可以同時在一個連接上傳輸
- Message: 類似於 HTTP/1.1 的請求和回應
- Frame: 最小的傳輸單位,所有的訊息都被分割成多個 Frame 傳輸
並帶來了以下幾個重要的改進 :
- Multiplexing: 允許在同一個 TCP 連接上同時傳輸多個請求和回應,解決了 Head-of-Line Blocking 在 HTTP 層級的問題
- Stream Prioritization: 允許客戶端指定請求的優先級,讓伺服器可以根據優先級來分配資源
- HPACK: 透過將常用的標頭欄位進行壓縮,減少重複傳輸的資料量
- Server Push: 允許伺服器主動推送資源到客戶端,舉例來說,當客戶端請求一個 HTML 文件時,伺服器可以同時推送相關的 CSS 和 JavaScript 檔案,減少額外的請求延遲
HTTP/3
前面提到 HTTP/2 改為採用了單一 TCP 連接來進行 multiplexing,雖然解決了 HTTP 層級的 Head-of-Line Blocking,但反而加劇了 TCP 層級的 Head-of-Line Blocking 問題。 即使兩個封包屬於不同的 stream 中,TCP 依然會因為一個封包遺失而阻塞其他 stream 的傳輸,這讓 HTTP/2 的優勢大打折扣。
除此之外,現代網路多會採用 HTTPS,也就是在 TCP 之上加了一層 TLS,這又增加了連接建立的延遲,因為 TCP 握手需要 1 RTT,TLS 握手通常需要 1-2 RTT,總共可能需要 2-3 RTT 才能完成連接建立。
為了解決這些問題,HTTP/3 採用了全新的傳輸協定 QUIC,這是一個基於 UDP 的協定,並在應用層實作了類似 TCP 的可靠傳輸機制,同時內建了 TLS 1.3。
QUIC 帶來了以下幾個重要的改進 :
- stream-level multiplexing: 在傳輸層實作了 connection + stream 的概念,解決了 TCP 層級的 Head-of-Line Blocking
- faster connection establishment: 結合了握手過程,首次連接只需 1 RTT,後續連接甚至可以達到 0-RTT
- connection migration: 由於 QUIC 使用 connection ID 來識別連接而非傳統的 ip:port,因此可以在網路切換時保持連接不中斷
- improved congestion control: 在應用層重新實作 flow control 和 congestion control,使用更現代化的演算法 (例如 CUBIC、BBR) 來提升效能
但同時,QUIC 也帶來不少挑戰,像是防火牆和中間設備可能會阻擋 UDP 流量,並且會導致 CPU 使用率增加等等。