
HTTP 內容壓縮:透過 Accept-Encoding 減少傳輸量
前面看完 HTTP 版本與 redirect 後,接著來處理 response body 本身。瀏覽器跟伺服器之間可以透過 HTTP 內容壓縮,減少回應內容實際傳輸的資料量。瀏覽器收到回應後會依照 Content-Encoding 解壓縮,再把原本的內容交給後續流程使用,藉此縮短下載時間並節省頻寬。
不過,這項機制只會縮小網路上傳輸的內容,不會直接優化原始檔案。假如 JavaScript bundle 或圖片本身太大,還是要從拆分程式碼、調整圖片尺寸與格式等方向著手!
如何設定
瀏覽器會透過 request header Accept-Encoding 告訴伺服器可以接受哪些 content coding。以下範例表示瀏覽器可以接受 Brotli 與 gzip;沒有設定 q 值時,兩者的預設權重都是 1:
Accept-Encoding: br, gzip伺服器會依照瀏覽器的偏好、自己支援的演算法與當下策略選擇格式。假如這次選擇 Brotli,回應標頭就會帶上:
Content-Encoding: br如果伺服器不支援 br,但支援 gzip,就可以改用 gzip;也可能因為檔案太小、內容不適合壓縮或伺服器負載等原因,直接回傳未壓縮的內容。Accept-Encoding 表示「可以接受」,不代表伺服器一定要壓縮。
透過 Vary 區分不同的壓縮版本
當 Accept-Encoding 同時列出多個格式時,並不是要求伺服器同時使用這些格式,而是表示瀏覽器都能接受。伺服器會選擇其中一個,並透過 Content-Encoding 說明這次回應實際使用的格式。
Accept-Encoding: br, gzip如果伺服器選擇 Brotli,回應可能會是:
Content-Encoding: br
Vary: Accept-Encoding其中 Content-Encoding: br 表示這次回應使用 Brotli 壓縮,瀏覽器會使用 Brotli 解壓縮;gzip 在這次請求中只是另一個可接受的選項,並不會同時使用。
Vary: Accept-Encoding 不負責選擇壓縮格式,也不會讓瀏覽器額外儲存 br。它是告訴瀏覽器快取、反向代理或 CDN:快取這個 URL 的回應時,除了 URL 之外,也要參考請求中的 Accept-Encoding。
因為同一個 URL 可能有不同的壓縮版本,例如:
Accept-Encoding: br, gzip → Content-Encoding: br
Accept-Encoding: gzip → Content-Encoding: gzip當快取收到第二個請求時,就不應直接把第一個 Brotli 版本拿來使用,因為該請求沒有宣告支援 Brotli。Vary 讓快取知道這些回應必須被視為不同版本,避免把瀏覽器無法解壓縮的內容提供出去。
這個問題最常見於 CDN、反向代理等共享快取;同一臺裝置上使用不同瀏覽器、舊版 WebView,或不同請求元件時,也可能出現不同的 Accept-Encoding。
實現檔案壓縮的方法
其實後端跟 Web Server 都能處理回應壓縮。以 JavaScript 生態來說,Express 跟 Koa 都可以透過 middleware 實作;也可以交給 NGINX、反向代理或 CDN 統一處理。
這裡談的是回應壓縮,跟後端是否需要解壓縮 request body 是兩件不同的事。當流量較大時,把壓縮放在反向代理或 CDN,通常能減少應用程式處理回應壓縮的 CPU 成本;但最後還是要依照部署架構、動態內容比例與快取策略來決定。
以 NGINX 為例,可以加入以下設定:
server {
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/javascript
application/json
application/xml;
}gzip_types 用來指定除了 text/html 以外要壓縮的 MIME type;gzip_min_length 可以避免壓縮太小、效益有限的回應;gzip_vary on 則會在啟用 gzip 時加入 Vary: Accept-Encoding。範例將壓縮等級設為 6,實際上仍要測量 CPU 成本與壓縮後大小,並不是設成最高的 9 就一定比較快。
另外,這段 NGINX 設定只會啟用 gzip。如果還要提供 br,必須另外確認 NGINX 建置、額外模組或前置 CDN 是否支援 Brotli。JPEG、PNG、WebP 等本身已經壓縮過的格式,通常也不適合再使用 gzip 壓縮。
總結
HTTP 內容壓縮主要是為了減少瀏覽器與伺服器之間的傳輸量,並不會改變解壓縮後的實際內容。瀏覽器會透過 Accept-Encoding 告訴伺服器支援哪些壓縮格式,伺服器選擇合適的格式後,再使用 Content-Encoding 告訴瀏覽器該如何解壓縮。
如果同一個 URL 可能因為瀏覽器支援的壓縮格式不同,而產生不同版本的回應,就需要搭配 Vary: Accept-Encoding,讓瀏覽器快取、反向代理或 CDN 正確區分 br、gzip 等版本,避免提供瀏覽器無法解壓縮的內容。
實作上可以交由 Express、Koa 等後端 middleware 處理,也可以放在 NGINX 或 CDN 這類 Web Server 層級統一處理。不管選擇哪一層,壓縮格式、檔案類型、檔案大小與壓縮等級都要實際測量,才能在傳輸量與 CPU 成本之間找到適合的平衡。
這裡提到的 Vary: Accept-Encoding 也會直接影響 cache 如何區分同一個 URL 的不同 response。下一篇瀏覽器快取機制會接著整理 freshness、revalidation 與 Cache Busting 的運作方式。