帮助中心

服务器、网络和账户的分步教程。

Linux 服务器

小内存 VPS 上的 Nginx 性能优化:Worker、Keepalive、Gzip/Brotli、HTTP/2、HTTP/3 与缓存

小内存 VPS 上的 Nginx 性能优化:Worker、Keepalive、Gzip/Brotli、HTTP/2、HTTP/3 与缓存

在 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 的差异说明),并介绍如何测量效果。

开始之前请确认 HTTPS 已经可用,参见SSL 证书安装教程。先备份配置: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/

记下每秒请求数和延迟,每做一项调整后用同样的命令再测一次。

只对自己的网站做压测,并且控制时长。JUSTG 云服务器允许短时间 100% CPU 峰值,但 1 小时平均 CPU 超过 50% 会被自动限制在 50%。

第 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_connectionsworker_rlimit_nofile
mini(1 核,512 MB)10244096
01(2 核,1 GB)20488192
02(3 核,2 GB)409616384

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
如果网站套了 Cloudflare 等 CDN 代理,访客连接的是 CDN,HTTP/3 由 CDN 负责提供。这时在源站开启 HTTP/3 意义不大。

第 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 也不会缓存。

绝不要缓存显示个人数据的页面。如果你的应用有自己的登录 Cookie,请把 Cookie 名加到 $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 客服(工单)。

这篇文章有帮助吗?
帮助中心
Linux 服务器 Linux SSH 密钥登录教程:设置 ed25519 密钥并禁用密码登录 只要服务器有公网 IPv4,上线几分钟内就会遇到 SSH 暴力破解机器人,而密码登录正是它们的首要目标。本文说明如何用较新的 ed25519 密钥完成 Linux SSH 密钥登录,再禁用 SSH 密码登录,全程避免把自己锁在门外。步骤适用于 Debian… Linux 服务器 Linux VPS 修改 SSH 端口与 root 密码教程(安全不断线) 把 SSH 从 22 端口移走虽然挡不住有心的攻击者,却能让日志里大部分自动扫描消失;而任何新服务器的第一件事,就是换一组新的 root 密码。本文说明如何在 Linux VPS 修改 SSH 端口与 root 密码且不断线:修改 sshd_config、在 Rocky… Linux 服务器 Linux 防火墙设置教程:VPS 使用 UFW 与 firewalld(含 IPv6) 新安装的 Linux 服务器,任何服务打开的端口都会直接对外响应,因此主机防火墙是最先要设置的项目之一。本文说明如何在 Debian / Ubuntu 用 UFW 设置 Linux 防火墙,以及在 CentOS、Rocky Linux、AlmaLinux 使用… Linux 服务器 Linux VPS 添加 Swap 交换空间教程(适合 512MB / 1GB 小内存套餐) 在 512 MB 或 1 GB 的云服务器上,数据库、PHP-FPM 加上一次软件包更新,很容易在同一时间用光内存。这时内核的 OOM killer 会强制终止进程,最常被牺牲的就是 MySQL 或 MariaDB。在 Linux VPS 添加 Swap… Linux 服务器 Linux 硬盘空间已满怎么办?VPS 找出并释放磁盘空间教程 “No space left on device”几乎会让服务器上所有东西出问题:数据库无法写入、网站返回错误、软件包更新失败,有时甚至无法正常登录。本文一步步说明如何排除 Linux 硬盘空间已满:先确认是数据块还是 inode 用完,再用 du 与 ncdu…
AlipayUnionPayVISAMastercardPayPalUSDTstripe