在 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 客服(工单)。