结论: 配置 Nginx 的关键是理解它的层级结构——main 管进程、events 管连接、http 管全局、server 管站点、location 管路由。实操顺序为:装好后先调 /etc/nginx/nginx.conf 的性能参数,再为每个站点建独立 server 块,按 location 优先级写路由规则,最后用反向代理对接后端应用并套上 SSL。
引言:Nginx 为什么是服务器的标配
Nginx 是目前最主流的 Web 服务器与反向代理(Reverse Proxy),以事件驱动(Event-Driven)架构著称,同样的硬件能支撑数倍于传统多进程模型的并发连接。在服务器上它通常承担四件事:托管静态文件、反向代理到后端应用(Node.js、Java Spring Boot、Python Gunicorn)、做负载均衡(Load Balancing)、终止 SSL 并做缓存。
绝大多数线上故障不是 Nginx 本身的问题,而是配置层级理解错误:把只该出现在 http 的指令写进 server、把 location 匹配规则写得互相覆盖、或者改完不 nginx -t 就直接 reload。本文按"结构 → 站点 → 路由 → 代理 → 优化"的顺序讲清每个环节。示例基于 Ubuntu 24.04 LTS 与 Nginx 1.26(截至 2026 年主流稳定版为 1.26 / 1.27)。
Nginx 配置文件结构与目录
结论:Nginx 的配置是树形上下文(Context)结构,每条指令只能出现在特定的上下文中,写错位置会直接报 directive is not allowed here。
| 上下文 | 作用 | 常见指令 | 出现次数 |
|---|---|---|---|
| main | 全局进程级 | user、worker_processes、error_log、pid | 1 |
| events | 连接处理模型 | worker_connections、use、multi_accept | 1 |
| http | HTTP 全局 | include、gzip、log_format、upstream、server | 1 |
| server | 虚拟主机 | listen、server_name、root、ssl_* | 多个 |
| location | URL 路由 | proxy_pass、try_files、expires、deny | 多个 |
| upstream | 后端服务器组 | server、least_conn、keepalive | 多个 |
Ubuntu/Debian 的目录约定如下,读懂它能少走很多弯路:
/etc/nginx/nginx.conf:主配置文件,包含全局与http块。/etc/nginx/sites-available/:可用站点配置(写在这里)。/etc/nginx/sites-enabled/:已启用站点(由sites-available软链过来)。/etc/nginx/conf.d/*.conf:http块末尾自动 include 的目录,CentOS 系默认用这个。/var/log/nginx/access.log、error.log:访问与错误日志。
安装与基础检查命令:
sudo apt update && sudo apt install -y nginx
sudo systemctl enable --now nginx
nginx -v # 查看版本
nginx -T # 打印最终生效的完整配置(排错神器)
sudo nginx -t # 语法检查,改配置后必做
sudo systemctl reload nginx # 平滑重载,不中断连接第一步:调优主配置文件
结论:Nginx 开箱即用的参数偏保守,生产环境至少要调 worker_processes、worker_connections、keepalive_timeout 与 gzip 四项。下面是一份可直接套用的 /etc/nginx/nginx.conf。
user www-data;
worker_processes auto; # 自动设为 CPU 核心数
worker_rlimit_nofile 65535; # 单进程可打开的最大文件数
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
use epoll; # Linux 下性能最好的事件模型
worker_connections 10240; # 每个 worker 的最大并发连接
multi_accept on; # 一次尽可能多地接受新连接
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
sendfile on; # 零拷贝发送静态文件
tcp_nopush on; # 配合 sendfile,减少报文数量
tcp_nodelay on; # 小包立即发送,降低延迟
server_tokens off; # 隐藏版本号,降低被针对性攻击的风险
keepalive_timeout 65; # 长连接超时(秒)
keepalive_requests 1000; # 单条长连接最多处理的请求数
client_max_body_size 64m; # 请求体上限,影响上传大小
client_body_timeout 30s;
reset_timedout_connection on;
# ---- gzip 压缩 ----
gzip on;
gzip_vary on;
gzip_comp_level 6;
gzip_min_length 1024; # 小于 1KB 不压缩,避免得不偿失
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript
application/x-javascript text/xml application/xml application/xml+rss
text/javascript image/svg+xml font/woff2;
# ---- 文件描述符缓存 ----
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}几个容易算错的数值:
- 理论最大并发 ≈ worker_processes × worker_connections。4 核 × 10240 ≈ 4 万并发连接。但作为反向代理时每个客户端连接会占用一个到后端的连接,实际承载的客户端数约为该值的一半。
worker_rlimit_nofile必须 ≥worker_connections的 1~2 倍,否则会报too many open files;同时要确认系统ulimit -n已放宽。gzip_comp_level超过 6 之后压缩率提升极小但 CPU 消耗陡增,6 是性价比拐点。gzip_types里不需要写text/html,Nginx 默认就会压缩 HTML。
第二步:server 块与虚拟主机
结论:一个 server 块就是一个虚拟主机,靠 listen + server_name 区分。一台服务器托管多个域名,只要建多个 server 块即可。
下面是一份典型的站点配置,包含 HTTP 跳转 HTTPS、静态资源缓存、PHP 转发:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri; # 全站跳转 HTTPS
}
server {
listen 443 ssl;
http2 on; # Nginx 1.25.1+ 写法;老版本用 listen 443 ssl http2
server_name example.com www.example.com;
root /var/www/example.com;
index index.html index.php;
access_log /var/log/nginx/example.access.log main;
error_log /var/log/nginx/example.error.log warn;
# 静态资源强缓存 + 关闭日志,减少磁盘写
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# PHP 交给 PHP-FPM
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_read_timeout 300s;
}
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}要点说明:
server_name支持通配符(*.example.com)和正则,精确名称优先级最高。- 没有匹配到任何
server_name的请求会落到该端口的默认 server(第一个定义的,或标了default_server的那个)。 try_files是伪静态与前端路由的标配,Vue/React 单页应用通常写成try_files $uri $uri/ /index.html;。return 301比rewrite效率更高,纯跳转一律用return。
第三步:location 匹配规则与优先级
结论:location 的匹配顺序是 Nginx 最容易被搞错的地方。规则只有一条主线:先精确,再前缀,最后正则。
| 写法 | 类型 | 含义 | 优先级 |
|---|---|---|---|
location = /path | 精确匹配 | 完全一致才命中,命中即终止 | 1(最高) |
location ^~ /path | 优先前缀 | 前缀匹配且跳过正则检查 | 2 |
location ~ \.php$ | 正则(区分大小写) | 按出现顺序依次尝试 | 3 |
location ~* \.PNG$ | 正则(不区分大小写) | 同上 | 3 |
location /path | 普通前缀 | 取最长者,正则全落空才用 | 4(最低) |
完整判定流程:
- 用
=精确匹配,命中立即使用; - 对所有前缀 location 试匹配,记住最长的那个;
- 若最长前缀是
^~,立即使用并停止; - 否则按配置文件顺序逐个测试正则,第一个命中的即用;
- 正则全不命中,才使用第 2 步记住的最长前缀。
典型配置示例:
location = / {
# 只匹配首页
}
location ^~ /static/ {
alias /data/static/; # alias 会替换掉 location 前缀,root 则是拼接
expires 7d;
}
location ~* \.(gif|jpg|jpeg|png|webp)$ {
expires 30d;
add_header Cache-Control "public";
}
location /api/ {
proxy_pass http://backend/; # 注意末尾的 / 会把 /api/ 前缀去掉
}
location / {
try_files $uri $uri/ =404;
}特别注意 alias 与 root 的区别:root /data/static 会把 /static/a.jpg 解析为 /data/static/static/a.jpg,而 alias /data/static 解析为 /data/static/a.jpg。这是最常见的 404 来源。
第四步:反向代理配置
结论:反向代理的作用是把外部请求转发给内部应用(如 8080 端口的 Java 服务),同时隐藏后端。核心是 proxy_pass 加一组转发头,缺了 Host 与 X-Forwarded-* 后端拿不到真实信息。
upstream backend {
least_conn; # 最少连接调度
server 127.0.0.1:8080 weight=5 max_fails=3 fail_timeout=15s;
server 127.0.0.1:8081 weight=5 max_fails=3 fail_timeout=15s;
keepalive 64; # 与后端保持的长连接数
}
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection ""; # 配合 keepalive,必须清空
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_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 16 8k;
}
# WebSocket 支持
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
}避坑提示:
proxy_pass后面带不带/结果完全不同。若 location 是/api/且proxy_pass http://backend/(带斜杠),转发时/api/前缀会被剥掉;不带斜杠则原样透传。- 后端要拿到真实客户端 IP,除了转发头,还需在应用侧配置信任代理(如 Spring Boot 的
server.forward-headers-strategy=native)。 - 上传大接口要同步调大
client_max_body_size,否则报 413。
第五步:SSL/TLS 与性能优化收尾
结论:HTTPS 已成为默认要求,现代 Nginx 只需启用 TLS 1.2/1.3、关闭不安全套件并开启会话复用,安全性与性能可以兼得。
server {
listen 443 ssl;
http2 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_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305;
ssl_session_cache shared:SSL:20m; # 约可缓存 8 万个会话
ssl_session_timeout 1d;
ssl_session_tickets off; # 关闭 ticket 更安全
ssl_stapling on; # OCSP Stapling,减少客户端验证耗时
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
}最后压测验证,用 ab 或 wrk 观察 QPS 与错误率,再按瓶颈调整 worker_connections 或后端:
sudo nginx -t && sudo systemctl reload nginx
curl -I https://example.com
ss -lntp | grep nginx
tail -f /var/log/nginx/error.log
企业QQ咨询




