Nginx 性能调优完全指南:让你的网站飞起来
作为一个草根站长,网站访问速度直接决定了用户体验和搜索引擎排名。Nginx 作为目前最流行的 Web 服务器,默认配置其实只发挥了它 60% 左右的性能。本文把我自己在多个站点上实践过的 Nginx 调优经验完整分享出来,从最基础的配置到高级优化手段,一步一步带你调优,实测下来响应时间至少能缩短 30% 到 50%。
一、基准测试:先摸清家底再动手
调优之前一定要先做基准测试,这是很多人忽视的一步。没有基准数据,你根本不知道优化措施到底有没有效果,也不知道瓶颈在哪里。我推荐使用 ab(Apache Bench)工具做测试:
# 安装 ab
apt install -y apache2-utils
# 并发 100,总共发送 1000 个请求
ab -n 1000 -c 100 https://你的域名/
# 更接近真实用户场景的测试(持续 10 秒的压力测试)
ab -t 10 -c 50 https://你的域名/测试结果中重点关注以下几个指标。第一个是 Requests per second(每秒请求数),这个数字越大说明服务器的并发处理能力越强。第二个是 Time per request(平均每个请求的响应时间),这个数字越小用户体验越好。第三个是 Failed requests,任何失败请求都要排查原因。最后还有一个 Transfer rate(传输速率),这个反映了服务器的带宽利用情况。
测试完成后用文本保存结果,等调优完成后再测一次做对比。我的习惯是写一个简单的 Shell 脚本,一键执行基准测试并把结果追加到日志文件里:
#!/bin/bash
LOG=/var/log/nginx/benchmark.log
echo "=== $(date) ===" >> $LOG
ab -n 1000 -c 100 https://你的域名/ >> $LOG 2>&1
echo "" >> $LOG这样每次调优前后的数据都有记录,优化效果一目了然。
二、Worker 进程配置:最基础也最有效
Nginx 采用多进程模型,有一个 master 进程和多个 worker 进程。worker 进程的数量和配置直接影响并发处理能力。打开 /etc/nginx/nginx.conf,修改以下核心参数:
# 设置为 auto,Nginx 自动检测 CPU 核心数来决定 worker 进程数量
worker_processes auto;
# 每个 worker 的最大连接数
events {
worker_connections 10240;
use epoll;
multi_accept on;
}这几个参数需要重点理解一下。worker_processes auto 的意思是 Nginx 会按照 CPU 核心数启动对应数量的 worker 进程。比如一台 4 核的服务器就启动 4 个 worker 进程。这个数值不要超过 CPU 核心数,因为进程数超过核心数反而会因为上下文切换而降低性能,不如不设。
worker_connections 10240 表示每个 worker 进程最多能同时处理 10240 个连接。那么整台服务器的最大并发连接数就是 worker_processes × worker_connections。4 核 × 10240 = 40960 个并发连接,对于个人站长的中小型网站来说完全够用了。如果你的服务器是 2 核的,可以适当降低这个值,比如设到 5120,避免内存过度消耗。
use epoll 是 Linux 下最高效的 I/O 事件处理模型。Nginx 会自动检测并启用它,但显式写出来更保险一些。最后 multi_accept on 允许一个 worker 进程在接受新连接时一次性接受所有等待中的连接,而不是逐个接受,这样可以提高连接接受效率。
三、启用 Gzip 压缩:性价比最高的优化
Gzip 压缩绝对是性价比最高的优化手段,几乎不需要任何成本就能减少 60% 到 70% 的传输体积。在 http 块中添加以下配置:
http {
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
gzip_proxied any;
}几个关键参数需要根据实际情况调整。gzip_comp_level 的取值范围是 1 到 9,压缩级别越高压缩率越大但 CPU 消耗也越高。我实测过不同级别的效果:级别 2 压缩率约 40%,级别 6 约 55%,级别 9 约 57%。从 6 到 9 只提升了 2% 的压缩率,但 CPU 消耗翻了一倍。所以 6 是最佳的平衡点。
gzip_min_length 1k 的意思是文件小于 1KB 时不压缩。因为小文件压缩后反而可能变大(压缩头本身也有开销),得不偿失。gzip_types 只配置文本类型的文件就够了,图片、视频等二进制文件本身已经压缩过了,再压缩没有意义甚至可能变慢。
配置好之后,可以用 curl -H "Accept-Encoding: gzip" -I https://你的域名/css/style.css 查看响应头中是否包含 Content-Encoding: gzip,如果有就说明配置生效了。
四、静态文件缓存设置
对于 CSS、JS、图片等不经常变化的文件,让浏览器缓存起来可以大幅减少重复请求,对二次访问的速度提升非常明显:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|webp|woff2?|ttf|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
log_not_found off;
}这个配置的作用是告诉浏览器:这些文件可以缓存 30 天(expires 30d)。immutable 这个指令比较新,它告诉浏览器就算用户手动刷新页面,也不需要请求服务器验证文件是否过期,直接用缓存就行。access_log off 的意思是这些静态文件的请求不写入访问日志,可以节省磁盘 I/O 和日志文件的大小。log_not_found off 则是不记录文件不存在的 404 错误,避免大量爬虫请求不存在的图标文件时把日志撑爆。
五、开启 HTTP/2 提升加载性能
HTTP/2 协议相比 HTTP/1.1 有多个重要改进:多路复用允许在一个连接上同时传输多个请求和响应,解决了 HTTP/1.1 的队头阻塞问题;头部压缩大幅减少了请求头的大小;服务器推送允许服务器主动将资源推送给客户端。这些特性对页面加载速度的提升非常明显。
开启 HTTP/2 的前提是已经配置了 HTTPS:
server {
listen 443 ssl http2;
# HTTP 自动跳转到 HTTPS
listen 80;
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
}配置 HTTPS 必不可少的当然是 SSL 证书。推荐使用 acme.sh 配合 Let's Encrypt 自动申请和续签免费证书。配置好之后在 listen 行加上 http2 参数即可。注意 ssl_session_cache shared:SSL:10m 这个配置,它设置了 SSL 会话缓存的共享内存大小为 10MB。大约可以缓存 40000 个 SSL 会话,对于大多数站点来说完全够用。启用会话缓存后,客户端在第一次 SSL 握手后可以复用缓存的会话参数,后续连接只需要一次往返就能完成握手,显著减少了 HTTPS 的延迟开销。
六、FastCGI 缓存:让动态页面变静态
对于 PHP 动态站点,FastCGI 缓存是一项被很多人忽视但效果极其显著的技术。它把 PHP 生成的页面缓存到 Nginx 层,后续相同的请求直接从缓存返回,不再执行 PHP 脚本,响应速度可以提升 10 倍以上:
# 在 http 块中定义缓存路径和参数
http {
fastcgi_cache_path /tmp/nginx_fastcgi_cache levels=1:2 keys_zone=fcgicache:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
}
# 在 server 块中启用缓存
server {
set $skip_cache 0;
# 登录用户和评论提交不缓存(否则管理员看不到更新)
if ($http_cookie ~* "comment_author|wordpress_logged|typecho_auth") {
set $skip_cache 1;
}
# 后台页面不缓存
if ($request_uri ~* "/admin/|/login") {
set $skip_cache 1;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_cache fcgicache;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_use_stale error timeout invalid_header updating http_500;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}这个配置中,fastcgi_cache_path 的参数含义是:cache 文件存放在 /tmp/nginx_fastcgi_cache 目录,levels=1:2 表示目录层级结构,keys_zone=fcgicache:100m 表示分配 100MB 共享内存用于缓存键,inactive=60m 表示 60 分钟内没有被访问的缓存会被清理。
最后一行 add_header X-FastCGI-Cache $upstream_cache_status 非常有用,它会在响应头中增加一个 X-FastCGI-Cache 字段,值为 HIT 表示命中缓存,MISS 表示未命中,BYPASS 表示跳过缓存。用浏览器开发者工具就能看到,方便你调试缓存是否正常工作。
七、内核参数调优
Nginx 跑得再快,如果 Linux 内核限制了连接数也是白搭。以下是我在 Debian/Ubuntu 系统上常用的内核优化参数,加到 /etc/sysctl.conf 末尾:
# 最大文件打开数,Nginx 每个连接对应一个文件描述符
fs.file-max = 1000000
# 启用 TIME_WAIT 套接字重用
net.ipv4.tcp_tw_reuse = 1
# 缩短 FIN-WAIT-2 超时时间
net.ipv4.tcp_fin_timeout = 15
# TCP 连接队列大小
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
# 本地端口范围
net.ipv4.ip_local_port_range = 1024 65000
# TCP 读写缓冲区
net.core.rmem_default = 65536
net.core.wmem_default = 65536
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# 生效
sysctl -p同时修改 /etc/security/limits.conf,增加 Nginx 用户的文件打开数限制:
* soft nofile 1000000
* hard nofile 1000000
root soft nofile 1000000
root hard nofile 1000000修改完后需要重新登录或者重启 Nginx 服务才会生效。
八、监控与持续优化
调优不是一次性工作,需要持续监控和调整。我每天登录服务器都会看一下这几个指标:
# 查看 Nginx 活动连接状态
curl http://127.0.0.1/nginx_status
# 查看系统 TCP 连接状态统计
ss -s
# 实时查看请求日志中的响应时间
tail -f /var/log/nginx/access.log | awk '{print $NF}'要精确记录每个请求的处理时间,可以在 Nginx 配置中添加自定义日志格式:
log_format timed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log timed;这样每条日志都会记录请求总时间($request_time)和后端响应时间($upstream_response_time)。如果发现某个页面请求时间超过 2 秒,就需要排查是 PHP 执行慢还是数据库查询慢,还是 Nginx 本身配置有问题。
九、总结
Nginx 调优不是一蹴而就的事,建议按照本文的顺序逐步调整,每次只改一个参数,然后跑一次基准测试对比结果。我个人经验中最立竿见影的三个优化是:Gzip 压缩、HTTP/2 协议和 FastCGI 缓存。做好这三项,你的网站响应时间基本能减少一半。
最后提醒一个原则:不要在没做基准测试的情况下盲目套用别人的配置。每台服务器的硬件配置、运行的网站类型、用户的访问模式都不一样,适合别人的配置不一定适合你。先测再改,用数据说话,这是运维工作最基本也是最重要的原则。