在 512 MB 到 2 GB 記憶體的小型 VPS 上,只需幾項調整就能讓 Nginx 明顯變快:合理設定 worker,開啟客戶端和上游的 keepalive,啟用壓縮、TLS 會話複用、HTTP/2 或 HTTP/3,設定瀏覽器快取頭,再加上短時的 FastCGI 或反向代理快取。本文逐項講解,配置適用於 Debian 12/13 和 Ubuntu 22.04/24.04(附 Rocky Linux/AlmaLinux 的差異說明),並介紹如何測量效果。
sudo cp -a /etc/nginx /root/nginx-backup-$(date +%F)。第 1 步:測量基準資料
先測量,才能知道哪項調整真正有效。單個請求可以用 curl 看時間花在哪裡:
curl -so /dev/null -w 'dns %{time_namelookup}s connect %{time_connect}s tls %{time_appconnect}s ttfb %{time_starttransfer}s total %{time_total}s http/%{http_version}\n' https://example.com/
connect 減去 dns 大致就是網路 RTT,tls 是握手時間,ttfb(首位元組時間)減去 tls 是伺服器生成頁面所用的時間。如果 PHP 頁面的 ttfb 很高,問題在應用本身,這正是快取(第 7 步)最能發揮作用的地方。
測試負載下的吞吐量可以用 wrk、ab 或 hey,最好從另一臺機器發起,避免測試工具和 Nginx 搶 CPU:
# Debian/Ubuntu: sudo apt install wrk apache2-utils
# Rocky/Alma: sudo dnf install httpd-tools
wrk -t2 -c50 -d30s https://example.com/
ab -n 2000 -c 20 -k https://example.com/
hey -n 2000 -c 50 https://example.com/
記下每秒請求數和延遲,每做一項調整後用同樣的命令再測一次。
第 2 步:檢視 Nginx 版本和模組
nginx -v
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'http_v2|http_v3'
ls /etc/nginx/modules-enabled/ 2>/dev/null
nproc; free -m
grep -nE 'worker_|gzip|keepalive_timeout|access_log|ssl_session' /etc/nginx/nginx.conf
版本決定了能用哪些寫法:Debian 12 自帶 1.22,Debian 13 自帶 1.26,Ubuntu 22.04 自帶 1.18,Ubuntu 24.04 自帶 1.24。HTTP/3 需要 1.25 及以上版本,並且編譯時帶有 --with-http_v3_module;如果系統自帶的軟體包沒有這個模組,可以改用 nginx.org 官方源,官方的新版本已包含。最後一條命令會列出 nginx.conf 中已有的設定:在第 4 步之前,請把其中的 gzip on;、keepalive_timeout、access_log 以及 ssl_session_* 行註釋掉,否則 nginx -t 會報指令重複,或者每個請求被記錄兩次。
第 3 步:設定 worker 數量
# /etc/nginx/nginx.conf - main context (outside http {})
worker_processes auto;
worker_rlimit_nofile 8192;
events {
worker_connections 2048;
}
worker_processes auto 會按 CPU 核數啟動 worker。每個 worker 同時最多處理 worker_connections 個連線,反向代理的請求要佔兩個(客戶端一個、上游一個)。worker_rlimit_nofile 用來提高 worker 可開啟的檔案數上限,建議至少是 worker_connections 的兩倍。參考值:
| 套餐 | worker_connections | worker_rlimit_nofile |
|---|---|---|
| mini(1 核,512 MB) | 1024 | 4096 |
| 01(2 核,1 GB) | 2048 | 8192 |
| 02(3 核,2 GB) | 4096 | 16384 |
Nginx 本身每個連線佔用的記憶體很少。小套餐上真正的瓶頸通常是 PHP-FPM 或你的應用程式,請按實際記憶體調整它們的程序池大小。
第 4 步:Keepalive、壓縮、TLS 會話、檔案快取和日誌
把 http 層面的通用設定寫進一個檔案。Debian/Ubuntu 和 Rocky/Alma 都會在 http {} 中載入 /etc/nginx/conf.d/*.conf:
sudo tee /etc/nginx/conf.d/00-performance.conf > /dev/null <<'EOF'
# Loaded inside http {} by nginx.conf
# Keepalive with clients
keepalive_timeout 30s;
keepalive_requests 1000;
# Compression
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/javascript
application/json application/xml application/rss+xml image/svg+xml;
# TLS session reuse
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# Cache open file descriptors and metadata
open_file_cache max=2000 inactive=60s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# Buffered access log
access_log /var/log/nginx/access.log combined buffer=64k flush=5s;
EOF
- Keepalive:瀏覽器可以用同一條連線傳送多個請求,省去每次 TCP 和 TLS 握手。小伺服器上 30 秒是比較均衡的值,超時設得太長只會讓空閒連線一直佔著。
- Gzip:5 級壓縮能拿到大部分壓縮效果,CPU 開銷卻不大。
text/html預設總會壓縮,所以不用寫進列表。不要加入 JPEG、PNG 等圖片或壓縮包,它們本身已經壓縮過了。 - TLS 會話快取:回訪使用者可以直接恢復 TLS 會話,不用完整握手。10 MB 大約能存 4 萬個會話。
- open_file_cache:快取常用檔案的控制代碼和後設資料。釋出新版本後,修改過的檔案最多要
open_file_cache_valid(60 秒)才會被識別。 - 帶緩衝的訪問日誌:Nginx 每攢夠 64 KB 或每 5 秒寫一次日誌,而不是每個請求寫一次,可以減少磁碟寫入。日誌會有幾秒延遲。
Brotli(可選)
Brotli 對文字資源的壓縮率比 gzip 高約 15–20%,但它不屬於標準 Nginx。部分 Debian 和 Ubuntu 版本以動態模組的形式提供,請先確認:
apt-cache search libnginx-mod-http-brotli
sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static
如果搜尋沒有結果,說明你的系統版本沒有打包這個模組,Rocky/Alma 的基礎軟體源也沒有。這種情況下繼續用 gzip 就好,完全夠用。如果模組安裝成功,再加入:
# Add to /etc/nginx/conf.d/00-performance.conf only if the module is installed
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css text/xml text/javascript application/javascript
application/json application/xml application/rss+xml image/svg+xml;
第 5 步:上游 keepalive(反向代理)
如果 Nginx 反向代理到應用程式(Node.js、Python、Go 等),應保留一組到應用的空閒連線,而不是每個請求都新建連線:
upstream app_backend {
server 127.0.0.1:3000;
keepalive 16;
}
server {
# ... listen, server_name, certificates ...
location / {
proxy_pass http://app_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
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;
}
}
keepalive 16 表示每個 worker 保留的空閒連線數。上游 keepalive 必須配合 proxy_http_version 1.1 和清空的 Connection 頭才能生效。如果應用使用 WebSocket,請為對應的 location 單獨設定 Upgrade/Connection 頭。
第 6 步:開啟 HTTP/2 和 HTTP/3
HTTP/2 可以在一條連線上併發多個請求。HTTP/3 基於 QUIC(UDP),對丟包和網路切換的處理更好,對移動端和遠距離訪客尤其有幫助。Nginx 1.25.1 及以上版本的寫法:
server {
listen 443 ssl;
listen 443 quic reuseport;
http2 on;
http3 on;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
root /var/www/example.com;
}
- 在所有 server 塊中,同一個地址和連接埠的
reuseport只能出現一次。同一 IP 上的其他網站只寫listen 443 quic;。 - QUIC 固定使用 TLS 1.3,請保持
TLSv1.3開啟。 Alt-Svc響應頭告訴瀏覽器支援 HTTP/3:第一次訪問走 HTTP/2,之後的請求會切換到 HTTP/3。- 如果使用 IPv6,請用
[::]:443再加一組相同的 listen 行。
較舊的 Nginx(Ubuntu 22.04/24.04 和 Debian 12 自帶的版本)不支援 http2 on;,也不能用 HTTP/3。請在 listen 行開啟 HTTP/2:
listen 443 ssl http2;
HTTP/3 還需要在防火牆放行 UDP 443 連接埠(參見防火牆設定教程):
# Ubuntu/Debian with ufw
sudo ufw allow 443/udp
# Rocky/Alma with firewalld
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload
先檢查響應頭;如果你的 curl 支援 HTTP/3(curl -V 輸出中有 HTTP3),可以直接測試。否則請在瀏覽器開發者工具的“協議”一欄檢視是否顯示 h3:
curl -sI https://example.com/ | grep -i alt-svc
curl -sI --http3-only https://example.com/ | head -n 1
第 7 步:快取靜態檔案和動態頁面
靜態檔案的瀏覽器快取頭
# Static files: let browsers cache them
location ~* \.(?:css|js|mjs|woff2|svg|png|jpe?g|gif|webp|avif|ico)$ {
expires 30d;
access_log off;
try_files $uri =404;
}
# Versioned assets (file name contains a hash, e.g. app.3f9a1c.js)
location ^~ /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
add_header Alt-Svc 'h3=":443"; ma=86400' always;
access_log off;
}
一年期的 immutable 規則只適合檔名會隨內容變化的檔案,否則訪客會一直用舊版本。注意 Nginx 的一條規則:只要某個 location 裡有任何 add_header,它就不再繼承 server 塊裡的 add_header。所以這裡要重複寫一遍 Alt-Svc,HSTS 等安全響應頭也要同樣處理。
建立快取目錄
sudo mkdir -p /var/cache/nginx/fastcgi /var/cache/nginx/proxy
sudo chown -R www-data:www-data /var/cache/nginx # nginx:nginx on Rocky/Alma
PHP 的 FastCGI 微快取
PHP 輸出哪怕只快取 10 秒,也能把繁忙頁面從每秒執行幾百次 PHP 降到每 10 秒執行一次,而內容幾乎還是實時的:
# /etc/nginx/conf.d/10-fastcgi-cache.conf (http context)
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPCACHE:32m
max_size=256m inactive=10m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# Inside the server {} block of the PHP site
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($http_cookie ~* "PHPSESSID|wordpress_logged_in|wp-postpass|comment_author|woocommerce_items_in_cart") { set $skip_cache 1; }
if ($request_uri ~* "^/(wp-admin|wp-login\.php|cart|checkout|my-account)") { set $skip_cache 1; }
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_cache PHPCACHE;
fastcgi_cache_valid 200 301 302 10s;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache $upstream_cache_status always;
add_header Alt-Svc 'h3=":443"; ma=86400' always;
}
請把 socket 改成你的 PHP 版本:Debian 12 為 php8.2-fpm,Debian 13 為 php8.4-fpm,Ubuntu 22.04 為 php8.1-fpm,Ubuntu 24.04 為 php8.3-fpm。Rocky/Alma 上請使用 include fastcgi_params; 加 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;,socket 為 unix:/run/php-fpm/www.sock。已登入使用者、POST 請求、帶查詢引數的請求、購物車和後臺頁面都不會被快取。此外,設定了 Cookie 或帶有 Cache-Control: private、no-store 的響應,Nginx 也不會快取。
$http_cookie 那一行,上線前先以登入狀態測試。應用程式的 proxy_cache
# http context, e.g. /etc/nginx/conf.d/10-proxy-cache.conf
proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=APPCACHE:16m
max_size=512m inactive=60m use_temp_path=off;
# Add inside location / of the upstream example above
proxy_cache APPCACHE;
proxy_cache_key $scheme$host$request_uri;
proxy_cache_valid 200 301 1m;
proxy_cache_valid 404 10s;
proxy_cache_use_stale error timeout updating http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_cache_bypass $http_authorization $cookie_session;
proxy_no_cache $http_authorization $cookie_session;
add_header X-Cache $upstream_cache_status always;
帶 Authorization 頭或 session Cookie(請改成你應用實際使用的名稱)的請求會繞過快取。如果應用自己傳送 Cache-Control 或 Expires 頭,Nginx 會以它們為準,而不是 proxy_cache_valid。在 Rocky/Alma 上,SELinux 預設禁止 Nginx 連線本地連接埠,也可能攔截快取目錄,需要執行:
sudo setsebool -P httpd_can_network_connect 1
sudo restorecon -Rv /var/cache/nginx
第 8 步:測試並過載
sudo nginx -t && sudo systemctl reload nginx
curl -sI https://example.com/ | grep -iE 'x-cache|content-encoding|alt-svc'
nginx -t 會在生效前檢查語法;reload 在不斷開現有連線的情況下應用新配置。同一個頁面請求兩次,X-Cache 應從 MISS 變為 HIT。然後重新跑一遍第 1 步的基準測試做對比。如果 nginx -t 報錯,它會指出檔案和行號;修正後再試,或從 /root/ 恢復備份。
FAQ
哪項調整效果最明顯?
對 WordPress 等 PHP 網站來說,FastCGI 微快取的效果遠超其他項,因為大多數請求不再需要執行 PHP。對靜態網站來說,是壓縮和瀏覽器快取頭。worker 調優主要在併發訪客很多時才有明顯作用。
更新內容後怎麼清除快取?
刪除快取檔案後過載即可:sudo rm -rf /var/cache/nginx/fastcgi/*(或 proxy/*),然後執行 sudo systemctl reload nginx。如果只是 10 秒的微快取,一般等一會兒就行。
512 MB 記憶體的 VPS 跑 Nginx 夠用嗎?
Nginx 本身夠用:它提供靜態檔案和快取頁面只需要很少的記憶體。mini 套餐的瓶頸通常是 PHP-FPM 或資料庫,請把它們的程序池設小一些,讓快取來承擔流量。如需幫助,請提交工單,JUSTG 提供 7×24 小時 AI 客服(工單)。