VPSSPark 部落格
← 返回開發日記

徹底解決伺服器頻寬瓶頸:Nginx 反向代理調優實戰

機房手記 · 2026.07.22 · 約 14 分鐘閱讀

常見搜尋:Nginx 反向代理調優 · 伺服器頻寬優化 · gzip 壓縮 · proxy_cache · VPS 頻寬跑滿

伺服器機架與網路交換器——Nginx 反向代理與頻寬優化場景
頻寬瓶頸往往不是 CPU 不夠,而是 Nginx 沒把每一個位元組用對地方。

上個月我們幫一位跑 SaaS 後台的客戶排障:2 核 4G 的 VPS,CPU 常年 20%,但每到下午三點網站就卡成投影片。雲端廠商監控裡出站頻寬曲線頂格,套餐 100 Mbps 被吃滿整整四十分鐘。他第一反應是「該升配了」——我們打開 Nginx 存取日誌,發現 70% 的流量是未壓縮的 JSON API 回應和帶 ?v= 隨機參數的靜態 JS,同一批資源被重複全量下發。

調完 Gzip、靜態快取標頭和 upstream keepalive 之後,峰值頻寬降到 35 Mbps,頁面載入時間從 8 秒縮到 1.2 秒,一分錢沒多花。這類故事在中小 VPS 上太常見了:瓶頸在出口頻寬,而不在算力。Nginx 作為反向代理站在使用者與後端之間,天然是「省頻寬」的最佳槓桿——前提是你要知道該擰哪幾顆螺絲。

本文不講 Nginx 安裝入門,而是把我們生產環境裡驗證過的一套頻寬導向調校清單整理出來:先診斷、再壓縮、再快取、最後優化連線複用與緩衝。前提是你已用 Nginx 做 443 反代(若還沒配防火牆,建議先看 雲端伺服器 UFW 防火牆設定指南,只開 22/80/443 再談優化)。

~65%
Gzip 後 JSON 體積典型降幅
4 步
診斷→壓縮→快取→連線
0 元
多數優化無需升配

為什麼頻寬會先爆,而不是 CPU?

雲端 VPS 的計費頻寬通常是雙向不對稱的:小套餐給 100 Mbps 峰值、月流量包 1–2 TB,看起來不少,但一個未壓縮的 500 KB JSON 介面,每秒 200 次請求就是 800 Mbps 的理論需求——瞬間打穿。與此同時,Nginx 處理靜態檔案和簡單反代,CPU 佔用往往很低,所以監控圖上會出現「CPU 很閒、頻寬很滿」的錯位。

另一個常見誤區是把所有流量都打到源站。沒有 CDN、沒有瀏覽器快取、沒有 proxy_cache,每個使用者每次重新整理都從 VPS 重新拉一遍 bundle.js 和 logo.png。Nginx 官方文件裡列出的 http 模組,本質上都是幫你「少傳位元組」或「少建連線」的工具箱——關鍵是按場景組合使用,而不是抄一份萬能設定就上線。

Nginx 反向代理頻寬優化四步法:診斷、壓縮、快取、連線複用
優先做零成本項(gzip、expires、keepalive),再考慮 CDN 或升級頻寬。
症狀 常見根因 優先動作
API 回應慢、頻寬高 JSON/HTML 未壓縮 開啟 gzip / brotli
重新整理頁面流量大 靜態資源無 Cache-Control expires + 指紋檔名
後端連線數飆高 每請求新建 upstream TCP keepalive 連線池
大檔案下載占滿出口 源站直傳、無分段快取 CDN 或 proxy_cache

第一步:先診斷,別盲目升頻寬

動手改設定前,花十分鐘確認「頻寬花在哪兒」。我們在伺服器上常用這組指令:

頻寬與連線診斷
# 即時按連線看流量(需 apt install iftop)
                sudo iftop -i eth0

                # 按小時/天統計(vnstat)
                vnstat -h
                vnstat -d

                # Nginx 存取日誌:找體積最大的 URL
                awk '{print $7, $10}' /var/log/nginx/access.log | \
                  awk '{a[$1]+=$2} END {for(i in a) print a[i], i}' | sort -rn | head -20

                # 目前 ESTABLISHED 連線數
                ss -s
                ss -tn state established | wc -l

若日誌裡排前幾名的是 /api/ 且回應體動輒幾百 KB,壓縮是第一優先級;若是 .js.css.woff2 且狀態碼全是 200(沒有 304),表示快取標頭沒配好。還要區分入站出站:DDoS 或爬蟲會把入站打滿,那是另一類問題(限速、WAF、fail2ban),本文聚焦正常業務下的出站優化。

雲端主控台裡的頻寬圖有 1–5 分鐘聚合延遲,排障時以伺服器本機 iftop 為準。若 SSH 都變卡,參考我們的 Linux 伺服器 SSH 連線被拒排查指南,先確認不是網路層故障。

第二步:Gzip / Brotli 壓縮——最低成本的頻寬削減

文字類回應(JSON、HTML、JS、CSS、SVG、XML)壓縮率通常在 60%–80%。Nginx 內建 gzip 模組,開起來只需幾行設定,對 CPU 的額外開銷在大多數 VPS 上完全可以接受。

/etc/nginx/nginx.conf 或 conf.d/gzip.conf
gzip on;
                gzip_vary on;
                gzip_proxied any;
                gzip_comp_level 5;          # 6–9 收益遞減,5 是性價比甜點
                gzip_min_length 256;        # 太小的回應不必壓
                gzip_types
                    text/plain
                    text/css
                    text/javascript
                    application/javascript
                    application/json
                    application/xml
                    image/svg+xml
                    font/woff2;

幾個實戰細節:第一,gzip_proxied any 確保反代後端回傳的內容也會壓縮(預設只壓直連回應)。第二,圖片(JPEG/PNG/WebP)和影片不要再 gzip——已經壓縮過的二進位,gzip 幾乎無效還浪費 CPU。第三,用 curl -H 'Accept-Encoding: gzip' -I https://your.site/api/foo 檢查回應標頭是否有 Content-Encoding: gzip

若編譯時帶了 Brotli 模組(部分發行版或自編譯版本),同等品質下 Brotli 比 gzip 再省 15%–20%,適合文字 API 密集型站點。沒有模組也不必強求,先把 gzip 配穩。

注意 HTTPS 與舊客戶端
極少數老舊客戶端對 gzip 的 HTTPS 回應處理有 bug;現代瀏覽器無此問題。若 API 要相容非常古老的嵌入式裝置,可對特定 User-Agent 關閉 gzip,但 2026 年絕大多數場景不必糾結。

第三步:靜態資源快取與 sendfile

前端建置工具(Vite、Webpack)會給檔名加 content hash,例如 app.a3f2b1.js。這種檔案可以長期快取,使用者第二次存取根本不走網路。問題出在很多團隊只做了建置側 hash,Nginx 側卻沒發 Cache-Control,瀏覽器每次仍發全量請求。

靜態資源 location 範例
location ~* \.(js|css|woff2?|ttf|ico|svg)$ {
                    root /var/www/app/dist;
                    expires 30d;
                    add_header Cache-Control "public, immutable";
                    access_log off;           # 靜態命中可關 access_log 減 IO
                }

                # 全域 http 區塊建議開啟
                sendfile on;
                tcp_nopush on;
                tcp_nodelay on;

sendfile 讓核心直接把檔案頁快取送到 socket,減少使用者態拷貝,對大靜態檔案和高併發下載都有幫助。注意:sendfile 與 AIO、執行緒池等進階特性組合時要測一遍,預設 trio(sendfile + tcp_nopush + tcp_nodelay)對絕大多數站點夠用。

HTML 和 index.html 不要用 immutable,否則發版後使用者可能看到舊殼子。通常策略是:hash 資源 30 天,HTML no-cache(允許協商快取)。

第四步:反向代理快取(proxy_cache)

對讀多寫少的 API(設定列表、商品目錄、文章詳情),可以在 Nginx 層做短 TTL 快取,源站頻寬直接除以快取命中率。這是 proxy 模組裡最被低估的能力之一。

proxy_cache 基礎設定
# http 區塊
                proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=apicache:50m
                                 max_size=2g inactive=60m use_temp_path=off;

                upstream backend {
                    server 127.0.0.1:3000;
                    keepalive 32;              # 見下一節
                }

                server {
                    location /api/ {
                        proxy_pass http://backend;
                        proxy_http_version 1.1;
                        proxy_set_header Connection "";

                        proxy_cache apicache;
                        proxy_cache_valid 200 5m;
                        proxy_cache_key "$scheme$request_method$host$request_uri";
                        add_header X-Cache-Status $upstream_cache_status;

                        proxy_buffering on;
                        proxy_buffers 16 16k;
                        proxy_busy_buffers_size 64k;
                    }
                }

$upstream_cache_status 會顯示 HITMISSBYPASS,排障時一眼可知快取是否生效。寫入操作(POST/PUT/DELETE)務必 bypass 快取;帶 Cookie 的個人化介面也要謹慎,避免把 A 使用者的資料快取給 B 使用者。

proxy_buffering 開啟時,Nginx 會先把後端回應收進緩衝區再發給客戶端——後端慢、客戶端快時,能避免後端連線長時間佔用;若後端是串流 SSE/大檔案上傳,要對特定 location 設 proxy_buffering off

第五步:upstream keepalive 減少 TCP 握手開銷

預設情況下,Nginx 對每個客戶端請求可能新建一條到後端的 TCP 連線。高 QPS 時,大量 TIME_WAIT 和三次握手會吃掉 CPU 和本機連接埠,間接讓回應變慢、有效頻寬下降。upstream keepalive 讓 Nginx 維護一個到後端的連線池,請求完成後連線歸還池中複用。

keepalive 必配三件套
upstream backend {
                    server 127.0.0.1:8080;
                    keepalive 64;               # 池大小,按 QPS 調整
                }

                location / {
                    proxy_pass http://backend;
                    proxy_http_version 1.1;     # HTTP/1.1 才能 keepalive
                    proxy_set_header Connection "";  # 清除 Connection: close
                }

缺少 proxy_http_version 1.1Connection "" 任一,keepalive 都不會生效——這是我們在客戶環境裡見過最多的「配了等於沒配」案例。池大小 keepalive 64 對日 PV 百萬級以下通常夠用,過大反而占後端檔案描述符。

第六步:worker、連線數與限速

頻寬打滿時,還要確認 Nginx 沒有成為連線數瓶頸。核心參數在 nginx.conf 頂層:

worker 與連線(2 核 VPS 參考)
worker_processes auto;
                worker_rlimit_nofile 65535;

                events {
                    worker_connections 4096;
                    use epoll;
                    multi_accept on;
                }

                # 可選:按 IP 限速,防單使用者占滿頻寬
                limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
                limit_conn_zone $binary_remote_addr zone=conn:10m;

                server {
                    limit_req zone=perip burst=20 nodelay;
                    limit_conn conn 50;
                }

worker_connections × worker_processes 要大於峰值併發連線數。2 核機器設 worker_processes auto 通常是 2,4096 × 2 = 8192 對中小站點綽綽有餘。同時把系統 fs.file-maxworker_rlimit_nofile 對齊,否則日誌裡會出現 too many open files

limit_rate 可對單連線限速(大檔案下載場景),limit_req 防刷介面。這些不直接「增加」頻寬,但能防止少數異常流量拖垮全體使用者——屬於頻寬治理的最後一道閘。

第七步:什麼時候該上 CDN?

當優化做完、頻寬仍週期性頂格,就該考慮 CDN 或升級套餐了。判斷標準很簡單:

  • 靜態資源占出站 60% 以上 → CDN 快取 JS/CSS/圖片,源站只回源 HTML 和 API
  • 使用者分布跨多個地區 → 邊緣節點縮短物理距離,等效降低源站頻寬壓力
  • 流量有突發(行銷活動、直播) → CDN 扛峰值,避免源站按峰值頻寬買套餐

上 CDN 後 Nginx 側記得:源站只接受 CDN 回源 IP(防火牆白名單),並透傳真實客戶端 IP(X-Forwarded-For / real_ip 模組)。否則攻擊者直連源站 IP,CDN 的防護和快取就 bypass 了。

完整 server 區塊範例(可直接改網域部署)

把上文零散設定合成一份最小可執行範例,適合 Node / Python 後端監聽 127.0.0.1:3000 的場景:

/etc/nginx/sites-available/app.conf
upstream app {
                    server 127.0.0.1:3000;
                    keepalive 32;
                }

                server {
                    listen 443 ssl http2;
                    server_name app.example.com;

                    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
                    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

                    gzip on;
                    gzip_types application/json application/javascript text/css text/plain;
                    gzip_min_length 256;

                    location /assets/ {
                        alias /var/www/app/dist/assets/;
                        expires 30d;
                        add_header Cache-Control "public, immutable";
                        access_log off;
                    }

                    location / {
                        proxy_pass http://app;
                        proxy_http_version 1.1;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header X-Forwarded-Proto $scheme;
                        proxy_set_header Connection "";
                    }
                }

改完後執行 sudo nginx -t && sudo systemctl reload nginx,用瀏覽器開發者工具 Network 面板確認靜態資源有 30 天快取、API 有 gzip。我們習慣在發版 checklist 裡加一條:curl -sI https://app.example.com/assets/main.js | grep -i cache

驗證與持續監控

調校不是一錘子買賣。建議在 Grafana / 雲端監控裡同時看這四條線:出站頻寬、Nginx active connections、upstream 回應時間、快取命中率(若開了 proxy_cache)。發版或流量活動前做一次對比壓測:ab -n 1000 -c 50 https://app.example.com/hey -n 5000 -c 100,記錄調校前後的頻寬峰值和 P95 延遲。

若頻寬降下來了但延遲仍高,瓶頸可能轉到後端資料庫或磁碟 IO——那是另一個話題。但至少你可以確定:錢應該花在加索引上,而不是盲目買 500 Mbps 套餐。

一句話總結
先 iftop 找大戶,再 gzip 壓文字、expires 快取靜態、keepalive 複用後端連線;讀多 API 加 proxy_cache;仍不夠再上 CDN。多數 VPS 頻寬問題,是設定問題而不是硬體問題。

調校之外:選對節點同樣省頻寬

Nginx 能幫你「少傳位元組」,但使用者到源站的物理距離沒法用設定消除。若你的受眾在亞太,卻買了一台美東 VPS,RTT 高、重傳多,有效吞吐天然打折。很多團隊會把建置與預覽放在雲端 Mac 或離使用者更近的節點,靜態資源走物件儲存 + CDN,源站只跑 API——頻寬帳單會好看很多。

VPSSPark 的 Mac mini M4 雲端節點適合跑 CI 打包、內網預覽和跳板維運:macOS 原生環境,Terminal 與 OpenSSH 開箱即用;待機功耗約 4W,適合 7×24 掛著當中轉或建置機,比再開一台 x86 小主機更省電。配合前文 UFW 只開 443、應用綁 127.0.0.1 的架構,攻擊面和頻寬浪費都能壓住。

如果你正在規劃一套「省頻寬、好維運」的部署架構, VPSSPark 雲端 Mac mini 是低功耗跳板與建置節點的務實選擇—— 立即了解套餐方案 ,讓 Nginx 調校成果不被錯誤的節點選址抵消。

限時特惠

頻寬優化好了——節點也得跟得上

低功耗 Mac mini · 原生 Unix 跳板環境 · 7×24 靜默在線

返回首頁
限時特惠 點擊查看方案