前言
在現代網頁效能優化中,壓縮技術扮演著至關重要的角色。Brotli 是由 Google 開發的新一代壓縮演算法,相較於傳統的 Gzip,能夠提供更高的壓縮率與更快的解壓縮速度。本文將詳細介紹如何在 Ubuntu 22.04 系統上為 Nginx 設定 Brotli 壓縮功能。
Brotli 壓縮演算法介紹
什麼是 Brotli?
Brotli 是 Google 於 2015 年發布的開源無損壓縮演算法,最初設計用於網頁字型壓縮(WOFF2 格式)。其名稱源自瑞士德語中的一種麵包「Brötli」。Brotli 採用了多種先進的壓縮技術:
- LZ77 演算法:用於尋找並消除重複的資料序列
- Huffman 編碼:進行熵編碼以減少位元數
- 二階上下文建模:根據上下文預測下一個符號
- 預定義字典:包含超過 120,000 個常見網頁內容的字串
Brotli 的優勢
- 更高的壓縮率:相同品質下,Brotli 比 Gzip 能多壓縮 15-25% 的檔案大小
- 更快的解壓縮速度:瀏覽器端解壓縮效能優異
- 專為網頁優化:內建的字典包含常見的 HTML、CSS、JavaScript 程式碼片段
- 靈活的壓縮等級:支援 0-11 級壓縮,可根據需求調整
Brotli 與 Gzip 的比較
| 特性 | Brotli | Gzip |
|---|
| 壓縮率 | 較高(約多 15-25%) | 標準 |
| 壓縮速度 | 較慢(高壓縮等級時) | 較快 |
| 解壓縮速度 | 快速 | 快速 |
| 瀏覽器支援 | 現代瀏覽器(需 HTTPS) | 所有瀏覽器 |
| CPU 使用率 | 較高(壓縮時) | 較低 |
| 壓縮等級 | 0-11 | 1-9 |
實際測試數據
以下是常見檔案類型的壓縮比較(原始檔案大小 100KB):
1
2
3
4
5
| 檔案類型 Gzip (等級 9) Brotli (等級 11) 節省空間
HTML 25.2 KB 21.3 KB 15.5%
CSS 18.7 KB 15.1 KB 19.3%
JavaScript 22.4 KB 18.6 KB 17.0%
JSON 19.8 KB 16.2 KB 18.2%
|
Nginx Brotli 模組安裝
Ubuntu 22.04 提供了包含 Brotli 模組的 nginx-extras 套件:
1
2
3
4
5
6
7
8
| # 更新套件清單
sudo apt update
# 安裝 nginx-extras(包含 Brotli 模組)
sudo apt install nginx-extras -y
# 驗證安裝
nginx -V 2>&1 | grep -o 'brotli'
|
方法二:使用第三方 PPA
如果需要更新版本的 Nginx 與 Brotli 模組:
1
2
3
4
5
6
7
8
9
| # 新增 ondrej/nginx PPA
sudo add-apt-repository ppa:ondrej/nginx -y
# 更新並安裝
sudo apt update
sudo apt install nginx libnginx-mod-brotli -y
# 確認模組已載入
nginx -V 2>&1 | grep -i brotli
|
方法三:從原始碼編譯
若需要完全控制編譯選項,可從原始碼編譯:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # 安裝編譯所需的相依套件
sudo apt install -y build-essential git libpcre3 libpcre3-dev \
zlib1g zlib1g-dev libssl-dev libgd-dev libgeoip-dev
# 下載 Brotli 模組原始碼
cd /usr/local/src
sudo git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
sudo git submodule update --init
# 下載 Nginx 原始碼(請使用與系統相同的版本)
NGINX_VERSION=$(nginx -v 2>&1 | grep -oP 'nginx/\K[\d.]+')
cd /usr/local/src
sudo wget http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
sudo tar -xzvf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}
# 取得現有 Nginx 的編譯參數
nginx -V 2>&1 | grep 'configure arguments'
# 編譯動態模組
sudo ./configure --with-compat --add-dynamic-module=/usr/local/src/ngx_brotli
sudo make modules
# 複製模組到 Nginx 模組目錄
sudo cp objs/ngx_http_brotli_filter_module.so /usr/share/nginx/modules/
sudo cp objs/ngx_http_brotli_static_module.so /usr/share/nginx/modules/
|
載入動態模組
如果使用動態模組,需要在 Nginx 設定檔中載入:
1
2
| # 編輯 /etc/nginx/nginx.conf
sudo nano /etc/nginx/nginx.conf
|
在檔案開頭(http 區塊之前)加入:
1
2
| load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
|
壓縮設定與參數調校
基本設定
在 /etc/nginx/nginx.conf 的 http 區塊中加入 Brotli 設定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| http {
# Brotli 壓縮設定
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_types
text/plain
text/css
text/javascript
text/xml
text/x-component
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/atom+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-font-opentype
application/x-font-truetype
font/eot
font/opentype
font/otf
font/ttf
image/svg+xml
image/x-icon
image/vnd.microsoft.icon;
# 設定最小壓縮檔案大小
brotli_min_length 256;
# 同時啟用 Gzip 作為備援
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/javascript
text/xml
application/javascript
application/json
application/xml
application/rss+xml
font/ttf
font/otf
image/svg+xml;
}
|
參數詳解
| 參數 | 說明 | 建議值 |
|---|
brotli | 啟用或停用 Brotli 壓縮 | on |
brotli_comp_level | 壓縮等級(0-11) | 4-6(平衡效能與壓縮率) |
brotli_static | 啟用預壓縮靜態檔案 | on |
brotli_types | 要壓縮的 MIME 類型 | 文字類型與字型 |
brotli_min_length | 最小壓縮檔案大小 | 256 位元組 |
brotli_buffers | 壓縮緩衝區設定 | 16 8k |
brotli_window | 滑動視窗大小 | 512k |
進階設定範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| http {
# Brotli 進階設定
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_min_length 256;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/vnd.ms-fontobject
application/wasm
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/bmp
image/svg+xml
text/cache-manifest
text/calendar
text/css
text/javascript
text/markdown
text/plain
text/xml
text/vcard
text/vnd.rim.location.xloc
text/vtt
text/x-component
text/x-cross-domain-policy;
# 根據位置設定不同壓縮等級
map $uri $brotli_level {
default 6;
~*\.json$ 4;
~*\.js$ 5;
~*\.css$ 5;
~*\.html$ 6;
~*\.svg$ 11;
}
}
|
靜態檔案預壓縮
為什麼要預壓縮?
即時壓縮會消耗 CPU 資源,對於不常變動的靜態檔案,預先壓縮可以:
- 減少伺服器 CPU 負載
- 使用更高的壓縮等級
- 加快回應速度
安裝 Brotli 命令列工具
1
| sudo apt install brotli -y
|
手動預壓縮
1
2
3
4
5
6
7
8
9
10
| # 壓縮單一檔案
brotli -k -q 11 /var/www/html/styles.css
# -k: 保留原始檔案
# -q: 壓縮品質(0-11)
# 批次壓縮目錄中的檔案
find /var/www/html -type f \( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.svg" -o -name "*.json" \) -exec brotli -k -q 11 {} \;
# 查看壓縮結果
ls -la /var/www/html/*.br
|
自動化預壓縮腳本
建立 /usr/local/bin/brotli-compress.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| #!/bin/bash
# Brotli 預壓縮腳本
WEB_ROOT="/var/www/html"
QUALITY=11
EXTENSIONS="html css js svg json xml txt"
echo "開始 Brotli 預壓縮..."
echo "目標目錄: $WEB_ROOT"
echo "壓縮品質: $QUALITY"
for ext in $EXTENSIONS; do
echo "處理 .$ext 檔案..."
find "$WEB_ROOT" -type f -name "*.$ext" | while read file; do
# 檢查 .br 檔案是否存在且比原始檔案新
if [ ! -f "${file}.br" ] || [ "$file" -nt "${file}.br" ]; then
brotli -k -q $QUALITY -f "$file"
echo "已壓縮: $file"
fi
done
done
echo "預壓縮完成!"
# 顯示壓縮統計
echo ""
echo "=== 壓縮統計 ==="
for ext in $EXTENSIONS; do
original=$(find "$WEB_ROOT" -type f -name "*.$ext" -exec stat -c%s {} + 2>/dev/null | awk '{sum+=$1} END {print sum}')
compressed=$(find "$WEB_ROOT" -type f -name "*.$ext.br" -exec stat -c%s {} + 2>/dev/null | awk '{sum+=$1} END {print sum}')
if [ -n "$original" ] && [ "$original" -gt 0 ]; then
ratio=$(echo "scale=2; (1 - $compressed / $original) * 100" | bc)
echo "$ext: 原始 $(numfmt --to=iec $original), 壓縮後 $(numfmt --to=iec $compressed), 節省 ${ratio}%"
fi
done
|
設定執行權限並加入排程:
1
2
3
4
5
| # 設定執行權限
sudo chmod +x /usr/local/bin/brotli-compress.sh
# 加入 crontab(每天凌晨 3 點執行)
echo "0 3 * * * /usr/local/bin/brotli-compress.sh >> /var/log/brotli-compress.log 2>&1" | sudo crontab -
|
Nginx 靜態 Brotli 設定
確保 brotli_static 已啟用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| server {
listen 443 ssl http2;
server_name example.com;
root /var/www/html;
# 啟用預壓縮檔案
brotli_static on;
gzip_static on;
location ~* \.(css|js|html|svg|json)$ {
# Nginx 會自動尋找 .br 檔案
brotli_static on;
gzip_static on;
# 快取設定
expires 1y;
add_header Cache-Control "public, immutable";
}
}
|
瀏覽器相容性
支援 Brotli 的瀏覽器
| 瀏覽器 | 最低版本 | 備註 |
|---|
| Chrome | 50+ | 2016 年起支援 |
| Firefox | 44+ | 2016 年起支援 |
| Safari | 11+ | 2017 年起支援 |
| Edge | 15+ | 2017 年起支援 |
| Opera | 36+ | 2016 年起支援 |
| iOS Safari | 11+ | 2017 年起支援 |
| Android Chrome | 50+ | 2016 年起支援 |
重要限制
- 僅支援 HTTPS:基於安全考量,瀏覽器只在 HTTPS 連線時才會接受 Brotli 壓縮
- Accept-Encoding 標頭:瀏覽器必須在請求中包含
br 編碼
檢查瀏覽器支援
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 前端檢測 Brotli 支援
function checkBrotliSupport() {
// 檢查是否為 HTTPS
if (location.protocol !== 'https:') {
console.log('Brotli 需要 HTTPS 連線');
return false;
}
// 現代瀏覽器通常都支援
// 可透過發送測試請求確認
fetch('/test.js', { method: 'HEAD' })
.then(response => {
const encoding = response.headers.get('Content-Encoding');
console.log('Content-Encoding:', encoding);
return encoding === 'br';
});
}
|
Nginx 回退設定
確保不支援 Brotli 的瀏覽器能正常使用 Gzip:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| http {
# Brotli(優先)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json;
# Gzip(回退)
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_types text/plain text/css application/javascript application/json;
# 根據 Accept-Encoding 自動選擇
# Nginx 會自動處理,優先使用 Brotli
}
|
效能測試與基準
使用 curl 測試壓縮
1
2
3
4
5
6
7
8
9
10
| # 測試 Brotli 壓縮
curl -H "Accept-Encoding: br" -I https://example.com/styles.css
# 測試 Gzip 壓縮
curl -H "Accept-Encoding: gzip" -I https://example.com/styles.css
# 比較回應大小
curl -H "Accept-Encoding: br" -so /dev/null -w '%{size_download}' https://example.com/styles.css
curl -H "Accept-Encoding: gzip" -so /dev/null -w '%{size_download}' https://example.com/styles.css
curl -so /dev/null -w '%{size_download}' https://example.com/styles.css
|
壓縮效能測試腳本
建立 /usr/local/bin/compression-benchmark.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| #!/bin/bash
# 壓縮效能基準測試
URL="https://example.com"
TEST_FILES=(
"/styles.css"
"/app.js"
"/index.html"
"/data.json"
)
echo "壓縮效能基準測試"
echo "=================="
echo ""
printf "%-30s %10s %10s %10s %10s %10s\n" "檔案" "原始" "Gzip" "Brotli" "Gzip%" "Brotli%"
printf "%s\n" "--------------------------------------------------------------------------------"
for file in "${TEST_FILES[@]}"; do
# 原始大小
original=$(curl -so /dev/null -w '%{size_download}' "${URL}${file}" 2>/dev/null)
# Gzip 大小
gzip_size=$(curl -H "Accept-Encoding: gzip" -so /dev/null -w '%{size_download}' "${URL}${file}" 2>/dev/null)
# Brotli 大小
brotli_size=$(curl -H "Accept-Encoding: br" -so /dev/null -w '%{size_download}' "${URL}${file}" 2>/dev/null)
# 計算節省百分比
if [ "$original" -gt 0 ]; then
gzip_saving=$(echo "scale=1; (1 - $gzip_size / $original) * 100" | bc)
brotli_saving=$(echo "scale=1; (1 - $brotli_size / $original) * 100" | bc)
else
gzip_saving="N/A"
brotli_saving="N/A"
fi
printf "%-30s %10s %10s %10s %9s%% %9s%%\n" \
"$file" "$original" "$gzip_size" "$brotli_size" "$gzip_saving" "$brotli_saving"
done
echo ""
echo "回應時間測試"
echo "============"
for file in "${TEST_FILES[@]}"; do
echo ""
echo "檔案: $file"
# 無壓縮
time_none=$(curl -so /dev/null -w '%{time_total}' "${URL}${file}" 2>/dev/null)
# Gzip
time_gzip=$(curl -H "Accept-Encoding: gzip" -so /dev/null -w '%{time_total}' "${URL}${file}" 2>/dev/null)
# Brotli
time_brotli=$(curl -H "Accept-Encoding: br" -so /dev/null -w '%{time_total}' "${URL}${file}" 2>/dev/null)
echo " 無壓縮: ${time_none}s"
echo " Gzip: ${time_gzip}s"
echo " Brotli: ${time_brotli}s"
done
|
使用 Apache Bench 進行負載測試
1
2
3
4
5
6
7
8
9
10
11
| # 安裝 Apache Bench
sudo apt install apache2-utils -y
# 測試 Brotli 壓縮效能
ab -n 1000 -c 10 -H "Accept-Encoding: br" https://example.com/styles.css
# 測試 Gzip 壓縮效能
ab -n 1000 -c 10 -H "Accept-Encoding: gzip" https://example.com/styles.css
# 測試無壓縮效能
ab -n 1000 -c 10 https://example.com/styles.css
|
監控 CPU 使用率
1
2
3
4
5
6
7
8
| # 安裝 sysstat
sudo apt install sysstat -y
# 監控 Nginx 程序 CPU 使用率
pidstat -p $(pgrep -d',' nginx) 1
# 或使用 htop 篩選 nginx 程序
htop -p $(pgrep -d',' nginx)
|
最佳實務
壓縮等級選擇指南
| 使用情境 | 建議等級 | 說明 |
|---|
| 即時壓縮 | 4-6 | 平衡 CPU 使用與壓縮率 |
| 預壓縮靜態檔案 | 11 | 最高壓縮率,一次壓縮多次傳輸 |
| 高流量網站 | 1-4 | 減少 CPU 負載 |
| API 回應 | 4-5 | 快速壓縮動態內容 |
完整的生產環境設定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
| # /etc/nginx/conf.d/compression.conf
# Brotli 設定
brotli on;
brotli_comp_level 6;
brotli_static on;
brotli_min_length 256;
brotli_buffers 16 8k;
brotli_window 512k;
brotli_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/wasm
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
font/otf
font/ttf
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/calendar
text/css
text/javascript
text/markdown
text/plain
text/xml
text/vcard
text/vtt
text/x-component
text/x-cross-domain-policy;
# Gzip 設定(回退)
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types
application/atom+xml
application/javascript
application/json
application/ld+json
application/manifest+json
application/rss+xml
application/vnd.geo+json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
font/otf
font/ttf
image/bmp
image/svg+xml
image/x-icon
text/cache-manifest
text/css
text/javascript
text/plain
text/xml
text/vcard
text/vtt
text/x-component;
|
故障排除
問題一:Brotli 未生效
檢查步驟:
1
2
3
4
5
6
7
8
9
10
11
| # 1. 確認模組已載入
nginx -V 2>&1 | grep brotli
# 2. 檢查設定檔語法
sudo nginx -t
# 3. 確認使用 HTTPS
curl -I https://example.com/styles.css | grep -i content-encoding
# 4. 檢查 Accept-Encoding 標頭
curl -H "Accept-Encoding: br, gzip, deflate" -I https://example.com/styles.css
|
常見原因:
- 未使用 HTTPS
- 模組未正確載入
- MIME 類型未包含在
brotli_types 中 - 檔案大小小於
brotli_min_length
問題二:CPU 使用率過高
解決方案:
1
2
3
4
5
6
7
8
| # 降低壓縮等級
brotli_comp_level 4;
# 增加最小壓縮大小
brotli_min_length 1024;
# 使用預壓縮
brotli_static on;
|
問題三:預壓縮檔案未使用
檢查步驟:
1
2
3
4
5
6
7
8
| # 確認 .br 檔案存在
ls -la /var/www/html/*.br
# 確認檔案權限
stat /var/www/html/styles.css.br
# 確認 Nginx 使用者可讀取
sudo -u www-data cat /var/www/html/styles.css.br > /dev/null && echo "OK"
|
問題四:與 CDN 的相容性
某些 CDN 可能會移除或修改壓縮設定。確保:
- CDN 支援 Brotli 壓縮
- 正確設定
Vary: Accept-Encoding 標頭 - CDN 快取考慮 Accept-Encoding 標頭
1
2
| # 確保 Vary 標頭正確設定
add_header Vary "Accept-Encoding";
|
效能優化檢查清單
結論
Brotli 壓縮是提升網站效能的有效手段,在 Ubuntu 22.04 上配合 Nginx 使用可以顯著減少傳輸大小,提升使用者體驗。透過本文的設定指南,您可以:
- 正確安裝並設定 Nginx Brotli 模組
- 根據網站特性調整壓縮參數
- 實施預壓縮策略以減少 CPU 負載
- 確保舊版瀏覽器的相容性
- 持續監控並優化壓縮效能
記住,壓縮只是網站效能優化的一環,結合快取策略、CDN 部署、圖片優化等技術,才能達到最佳的效能表現。
參考資源