本文用于建立 Nginx 的基本认识,并给中小型项目沉淀一套可复用的配置方式。

维护: 本文最后核对日期为 2026-06-09。Nginx 配置细节会随版本、发行版打包方式和模块启用情况变化。落地前应执行 nginx -vnginx -Vnginx -T 核对当前机器的实际配置。

Nginx 是什么

Nginx 常见角色:

角色 说明
静态文件服务器 直接返回 HTML、CSS、JS、图片、下载文件
反向代理 接收外部请求,转发给后端 Java / Node / Go 服务
HTTPS 入口 终止 TLS,管理证书和 HTTPS 跳转
负载均衡器 把请求分发给多个后端实例
TCP / UDP 代理 通过 stream 模块代理四层流量
简单网关 做域名、路径、Header、访问控制、日志等入口治理

不要把 Nginx 当成业务应用本身。它适合处理入口流量、连接、静态资源、反代、TLS 和轻量路由;业务权限、业务校验、业务审计仍然应该由应用系统负责。

核心运行模型

Nginx 的基本模型是:

1
2
3
4
master process
├── worker process
├── worker process
└── worker process

官方文档对这个模型的描述很直接:

  • master 负责读取和评估配置、维护 worker。
  • worker 负责实际处理请求。
  • Nginx 使用事件驱动模型处理连接。

常见配置:

1
2
3
4
5
6
7
8
9
10
11
user nginx;
worker_processes auto;

events {
worker_connections 1024;
}

http {
include mime.types;
include /etc/nginx/conf.d/*.conf;
}

中小型项目一般不用一开始就调很多性能参数。先把配置结构、域名匹配、反代、HTTPS、日志和 reload 流程做对。

配置文件结构

Nginx 配置由 directive 组成:

简单 directive:

1
worker_processes auto;

块 directive:

1
2
3
4
5
6
7
http {
server {
location / {
proxy_pass http://127.0.0.1:8080;
}
}
}

常见上下文:

上下文 位置 作用
main 顶层 全局配置,例如 userworker_processeserror_log
events 顶层 连接处理配置
http 顶层 HTTP 服务配置
server http 一个虚拟主机 / 站点 / 项目入口
location server 一个 URI 匹配规则
upstream http 一组后端服务
stream 顶层 TCP / UDP 代理配置

重点:配置片段放在哪个文件不重要,最终被 include 到哪个上下文才重要。

例如主配置里这样写:

1
2
3
http {
include /etc/nginx/conf.d/*.conf;
}

那么 /etc/nginx/conf.d/app.conf 里的内容就处在 http 上下文里,所以这个文件里可以直接写:

1
2
3
4
server {
listen 80;
server_name app.example.com;
}

但不能只写:

1
2
3
location /api/ {
proxy_pass http://127.0.0.1:8080;
}

因为 location 必须放在 server 里面。如果要拆 location 片段,应该在某个 server 内 include:

1
2
3
4
5
6
server {
listen 80;
server_name app.example.com;

include /etc/nginx/snippets/app-locations/*.conf;
}

插拔式配置

Nginx 的“插拔式配置”靠 include

官方 include directive 支持加载单个文件,也支持加载匹配 mask 的多个文件,例如:

1
2
include mime.types;
include vhosts/*.conf;

conf.d 风格

常见于 Nginx 官方包、CentOS / RHEL / 通用部署:

1
2
3
http {
include /etc/nginx/conf.d/*.conf;
}

新增项目:

1
2
3
sudo cp project-a.conf /etc/nginx/conf.d/project-a.conf
sudo nginx -t
sudo systemctl reload nginx

禁用项目:

1
2
3
sudo mv /etc/nginx/conf.d/project-a.conf /etc/nginx/conf.d/project-a.conf.disabled
sudo nginx -t
sudo systemctl reload nginx

注意:不要把备份文件命名成 project-a.conf.bak.conf,因为它仍然会被 *.conf 匹配。

sites-available / sites-enabled 风格

常见于 Debian / Ubuntu 风格:

1
2
3
http {
include /etc/nginx/sites-enabled/*;
}

新增项目:

1
2
3
4
sudo cp project-a.conf /etc/nginx/sites-available/project-a.conf
sudo ln -sf /etc/nginx/sites-available/project-a.conf /etc/nginx/sites-enabled/project-a.conf
sudo nginx -t
sudo systemctl reload nginx

禁用项目:

1
2
3
sudo rm /etc/nginx/sites-enabled/project-a.conf
sudo nginx -t
sudo systemctl reload nginx

sites-available 只是候选配置目录。真正是否加载,看主配置是否 include 了 sites-enabled,以及 sites-enabled 里是否有对应文件或软链接。

只选一种风格

同一个项目不要同时放:

1
2
/etc/nginx/sites-enabled/project-a.conf
/etc/nginx/conf.d/project-a.conf

如果主配置同时 include 了这两个目录,就会重复加载同一个 server

常见后果:

  • nginx -t 提示重复或冲突。
  • 某个 server_name 被忽略。
  • 请求进入默认站点。
  • 多个兜底 server 抢同一个 listen

确认实际加载了什么:

1
sudo nginx -T | grep -nE 'include|server_name|listen|project-a'

注意:nginx -T 会打印完整配置。不要把包含内部域名、证书路径、Basic Auth 文件路径、上游地址的输出随便贴到公开环境。

多个 server 如何工作

Nginx 支持多个 server 块。它会先按 listen 的地址和端口筛选,再按请求里的 Host 头匹配 server_name

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name a.example.com;
}

server {
listen 80;
server_name b.example.com;
}

server {
listen 80;
server_name c.example.com www.c.example.com;
}

请求:

1
Host: b.example.com

会进入 b.example.com 对应的 server

默认 server

如果请求没有匹配到任何 server_name,Nginx 会把请求交给当前 listen 地址和端口上的默认 server

显式配置默认站点:

1
2
3
4
5
6
server {
listen 80 default_server;
server_name _;

return 444;
}

这里的重点是:

1
listen 80 default_server;

不是:

1
server_name _;

server_name _ 只是一个常见占位名。它本身不代表通配全部域名。

每个项目单独 server 的前提

每个项目可以单独一个 .conf 文件,前提是项目之间有可区分的入口:

区分方式 示例
不同域名 a.example.comb.example.com
不同端口 :8081:8082
不同 IP 192.168.1.10:80192.168.1.11:80

如果多个项目共用同一个域名和端口,就不要拆成多个相同 server_nameserver。应该在同一个 server 里用不同 location 分流。

同域名多项目

同一个域名下挂多个项目:

1
2
https://example.com/app-a/
https://example.com/app-b/

推荐放在同一个 server

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name example.com;

location /app-a/ {
proxy_pass http://127.0.0.1:3001/;
}

location /app-b/ {
proxy_pass http://127.0.0.1:3002/;
}
}

不要写成两个完全相同域名的 server

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name example.com;
location /app-a/ {}
}

server {
listen 80;
server_name example.com;
location /app-b/ {}
}

这样会让同一个 listen + server_name 重复,后续行为不好维护。

location 匹配

server 选定后,Nginx 再用 URI 匹配 location

常见类型:

写法 含义
location = /health 精确匹配
location /api/ 前缀匹配
location ^~ /static/ 前缀匹配,命中后不再检查正则
location ~ \.php$ 区分大小写正则
`location ~* .(png jpg)$`
location / 兜底前缀

官方文档中的核心规则可以整理成:

  1. 先找最具体的前缀匹配。
  2. 再按配置顺序检查正则 location
  3. 如果正则命中,用第一个命中的正则。
  4. 如果正则没有命中,用之前找到的最长前缀。
  5. location 只匹配 URI 路径,不匹配 query string。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name example.com;

location / {
return 200 "root\n";
}

location /api/ {
return 200 "api\n";
}

location ~* \.(png|jpg)$ {
return 200 "image\n";
}
}

请求结果:

1
2
3
/api/users      -> /api/
/logo.png -> 正则图片 location
/about -> /

如果静态资源目录不想被正则覆盖,可以用 ^~

1
2
3
location ^~ /static/ {
root /var/www/example;
}

rootaliastry_files

root

root 是把 URI 追加到目录后面。

1
2
3
location /images/ {
root /data;
}

请求:

1
/images/a.png

实际文件:

1
/data/images/a.png

alias

alias 是把匹配到的路径前缀替换成另一个目录。

1
2
3
location /images/ {
alias /data/assets/;
}

请求:

1
/images/a.png

实际文件:

1
/data/assets/a.png

常见坑:

  • location /images/alias /data/assets/; 末尾斜杠要对应。
  • alias 更适合 URI 前缀和磁盘目录名不一致的场景。
  • 普通站点优先用 root,需要路径替换时再用 alias

try_files

静态站点和前端 SPA 常用:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name app.example.com;

root /var/www/app;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}
}

含义:

  1. 先找真实文件 $uri
  2. 再找目录 $uri/
  3. 都没有就回退到 /index.html

这适合 Vue / React Router 这类前端路由。否则刷新 /users/1 可能直接 404。

反向代理

最基础反代:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name api.example.com;

location / {
proxy_pass http://127.0.0.1:8080;
}
}

推荐加基础 Header:

1
2
3
4
5
6
7
8
location / {
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_pass http://127.0.0.1:8080;
}

后端服务需要正确处理这些 Header,尤其是:

  • 获取真实客户端 IP。
  • 生成正确的回调地址。
  • 判断当前请求是不是 HTTPS。

proxy_pass 末尾斜杠

这是 Nginx 最常见坑之一。

场景一:不带 URI。

1
2
3
location /api/ {
proxy_pass http://127.0.0.1:8080;
}

请求:

1
/api/users

后端收到:

1
/api/users

场景二:带 URI,并且末尾有斜杠。

1
2
3
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}

请求:

1
/api/users

后端收到:

1
/users

因为匹配到的 /api/ 被替换成了 proxy_pass 里的 /

规则建议:

  • 想让后端保留 /api 前缀:proxy_pass http://127.0.0.1:8080;
  • 想去掉 /api 前缀:proxy_pass http://127.0.0.1:8080/;
  • 正则 location 里使用 proxy_pass 时,优先不要在 proxy_pass 后面写 URI 部分。

WebSocket 代理

WebSocket 需要升级连接。

http 上下文放:

1
2
3
4
map $http_upgrade $connection_upgrade {
default upgrade;
"" close;
}

在对应 location 里:

1
2
3
4
5
6
7
8
9
10
11
12
location /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

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_pass http://127.0.0.1:8080;
}

如果少了 Upgrade / Connection,WebSocket 握手通常会失败。

负载均衡

多个后端实例可以用 upstream

1
2
3
4
5
6
7
8
9
10
11
12
13
upstream app_backend {
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}

server {
listen 80;
server_name api.example.com;

location / {
proxy_pass http://app_backend;
}
}

默认是 Round Robin。

常见方法:

1
2
3
4
5
6
upstream app_backend {
least_conn;

server 127.0.0.1:8081;
server 127.0.0.1:8082;
}

或者按客户端 IP 粘住:

1
2
3
4
5
6
upstream app_backend {
ip_hash;

server 127.0.0.1:8081;
server 127.0.0.1:8082;
}

注意:

  • 如果后端有登录状态,优先让应用无状态化,而不是依赖 ip_hash
  • ip_hash 在 NAT、代理、移动网络下不一定理想。
  • Nginx Open Source 主要是被动失败处理;主动健康检查通常是 NGINX Plus 能力或需要其他方案。

HTTPS 基础

HTTPS server 最小结构:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 443 ssl;
server_name example.com;

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

location / {
proxy_pass http://127.0.0.1:8080;
}
}

HTTP 跳 HTTPS:

1
2
3
4
5
6
server {
listen 80;
server_name example.com;

return 301 https://$host$request_uri;
}

要点:

  • 证书是公开信息,私钥必须限制权限。
  • 多个 HTTPS 域名共用一个 IP 时依赖 SNI。
  • server_name 要写真实域名,不要生产环境全用 _
  • TLS 协议和 cipher 默认值会随版本变化,老系统落地前必须核对当前 Nginx / OpenSSL 版本。
  • 证书自动续期后要 reload Nginx。

检查:

1
2
sudo nginx -t
sudo systemctl reload nginx

日志

常见日志:

1
2
/var/log/nginx/access.log
/var/log/nginx/error.log

基础配置:

1
2
access_log /var/log/nginx/app-access.log;
error_log /var/log/nginx/app-error.log warn;

更适合排查反代的 access log:

1
2
3
4
5
6
7
8
9
log_format proxy_access
'$remote_addr - $host "$request" '
'status=$status bytes=$body_bytes_sent '
'rt=$request_time '
'urt=$upstream_response_time '
'ua="$http_user_agent" '
'xff="$http_x_forwarded_for"';

access_log /var/log/nginx/app-access.log proxy_access;

字段含义:

字段 用途
$remote_addr Nginx 看到的客户端 IP
$host Host
$request 请求行
$status 响应状态码
$request_time Nginx 处理请求总耗时
$upstream_response_time 上游响应耗时
$http_x_forwarded_for 上游代理链路里的客户端 IP

常用查询:

1
2
3
4
5
6
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

grep "api.example.com" /var/log/nginx/access.log
grep " 502 " /var/log/nginx/access.log
grep "upstream" /var/log/nginx/error.log

如果应用日志里有 traceId,建议让前端或网关注入请求头,并在 Nginx 日志里记录:

1
2
3
4
log_format proxy_access
'$remote_addr - $host "$request" '
'status=$status rt=$request_time '
'trace_id="$http_x_trace_id"';

常用命令

查看版本:

1
2
nginx -v
nginx -V

检查配置:

1
sudo nginx -t

打印最终配置:

1
sudo nginx -T

重新加载:

1
sudo systemctl reload nginx

重启:

1
sudo systemctl restart nginx

查看状态:

1
systemctl status nginx

查看监听端口:

1
ss -lntp | grep nginx

查看配置是否被加载:

1
2
sudo nginx -T | grep -n "server_name"
sudo nginx -T | grep -n "project-a"

从本机带 Host 测试:

1
curl -i -H "Host: api.example.com" http://127.0.0.1/

测试后端:

1
curl -i http://127.0.0.1:8080/health

推荐配置结构

下面模板里的 proxy_access 需要先在 http 上下文定义 log_format proxy_access ...。如果暂时没有定义日志格式,可以先把 access_log 改成默认格式:

1
access_log /var/log/nginx/api-access.log;

单域名反代 Java 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
server {
listen 80;
server_name api.example.com;

access_log /var/log/nginx/api-access.log proxy_access;
error_log /var/log/nginx/api-error.log warn;

client_max_body_size 20m;

location / {
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_pass http://127.0.0.1:8080;
}
}

前端静态站点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80;
server_name app.example.com;

root /var/www/app;
index index.html;

access_log /var/log/nginx/app-access.log;
error_log /var/log/nginx/app-error.log warn;

location / {
try_files $uri $uri/ /index.html;
}

location ^~ /assets/ {
try_files $uri =404;
expires 7d;
}
}

前端加后端 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
listen 80;
server_name example.com;

root /var/www/app;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}

location /api/ {
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_pass http://127.0.0.1:8080;
}
}

这个配置会把 /api/users 原样转给后端。后端收到的路径仍然是 /api/users

如果后端不想要 /api 前缀,改成:

1
2
3
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}

最小上线流程

  1. 准备项目配置文件。
1
sudo vim /etc/nginx/conf.d/project-a.conf
  1. 检查语法。
1
sudo nginx -t
  1. 查看最终配置是否只加载一份。
1
sudo nginx -T | grep -n "project-a"
  1. reload。
1
sudo systemctl reload nginx
  1. 本机带 Host 测试。
1
curl -i -H "Host: project-a.example.com" http://127.0.0.1/
  1. 看日志。
1
2
tail -f /var/log/nginx/project-a-error.log
tail -f /var/log/nginx/project-a-access.log

常见问题

为什么新增 .conf 后没有生效

检查:

1
sudo nginx -T | grep -n "你的配置文件名"

常见原因:

  • 主配置没有 include 这个目录。
  • 文件后缀不是 .conf,但 include 写的是 *.conf
  • 放到了 sites-available,但没有软链接到 sites-enabled
  • 配置写在错误上下文里。
  • reload 前没有 nginx -t,配置根本没应用。

为什么访问域名进入了别的项目

检查:

1
2
sudo nginx -T | grep -nE "listen|server_name"
curl -i -H "Host: example.com" http://127.0.0.1/

常见原因:

  • server_name 没写真实域名。
  • 多个 server 使用了相同 listen + server_name
  • 请求的 Host 和你以为的不一致。
  • 没有匹配时进入了默认 server
  • 证书或 CDN 配置把流量打到了另一个入口。

为什么 502

502 通常说明 Nginx 连接上游失败或上游异常。

检查:

1
2
3
tail -f /var/log/nginx/error.log
curl -i http://127.0.0.1:8080/health
ss -lntp | grep 8080

常见原因:

  • 后端服务没启动。
  • proxy_pass 端口写错。
  • 后端只监听 127.0.0.1,但 Nginx 在容器或另一台机器。
  • 防火墙或安全组拦截。
  • 后端处理太慢,触发 proxy_read_timeout

为什么上传文件 413

默认 client_max_body_size 比较小。按业务设置:

1
2
3
server {
client_max_body_size 20m;
}

不要直接设置成无限大。上传接口还要在应用层限制文件类型、大小和鉴权。

为什么刷新前端路由 404

SPA 需要回退到 index.html

1
2
3
location / {
try_files $uri $uri/ /index.html;
}

为什么静态文件路径不对

先判断你要的是 root 还是 alias

root

1
2
3
location /images/ {
root /data;
}

/images/a.png -> /data/images/a.png

alias

1
2
3
location /images/ {
alias /data/assets/;
}

/images/a.png -> /data/assets/a.png

为什么 HTTPS 证书不对

常见原因:

  • 请求没有带 SNI。
  • server_name 和证书域名不匹配。
  • 进入了 443 的默认 server
  • 多个配置重复监听同一个域名。
  • 证书续期后没有 reload。

检查:

1
2
sudo nginx -T | grep -nE "listen 443|server_name|ssl_certificate"
openssl s_client -connect example.com:443 -servername example.com

最佳实践清单

配置组织

  • 每个项目一个 .conf 文件。
  • conf.dsites-enabled 二选一,不要同一个项目放两份。
  • 文件名清晰,例如 api-example-com.conf
  • 不要把备份文件留成 *.conf
  • 片段 include 要注意上下文:server 片段放 http 内,location 片段放 server 内。

server

  • 生产环境写真实 server_name
  • 只保留一个明确的 default_server
  • 不要多个项目都用 server_name _
  • 不同域名可以拆不同 server
  • 同域名不同路径优先放同一个 server,用 location 分流。

location

  • 少用复杂正则,能用前缀就用前缀。
  • 静态目录可用 ^~ 避免被正则覆盖。
  • location / 作为兜底。
  • 明确 proxy_pass 是否需要保留路径前缀。

反向代理

  • 统一传递 HostX-Real-IPX-Forwarded-ForX-Forwarded-Proto
  • 配置合理的 proxy_connect_timeoutproxy_read_timeout
  • 上传接口配置 client_max_body_size
  • 后端应用要信任代理头时,必须限制可信代理来源。

HTTPS

  • HTTP 到 HTTPS 用 301。
  • 证书私钥限制权限。
  • 证书续期后 reload。
  • 多域名 HTTPS 确认证书 SAN / wildcard / SNI。
  • 老服务器升级前核对 Nginx 和 OpenSSL 版本。

日志和排障

  • 每个重要项目建议有独立 access / error log。
  • 反代日志加 $request_time$upstream_response_time
  • 变更前 nginx -t
  • 变更后 nginx -T 核对最终配置。
  • 502 先看 Nginx error log,再看后端健康检查。

学习路线

按这个顺序学最稳:

  1. nginx -tnginx -T、reload、看日志。
  2. 理解 maineventshttpserverlocation
  3. 理解 include,知道配置是否真的被加载。
  4. 理解多个 server 的选择规则。
  5. 理解 location 匹配和 proxy_pass 路径变化。
  6. 能配置静态站点和 SPA。
  7. 能配置 Java 后端反代。
  8. 能配置 HTTPS 和 HTTP 跳转。
  9. 能排查 404、413、502、证书不匹配、Host 进错站点。
  10. 再学 upstream、缓存、限流、四层代理、HTTP/2、HTTP/3、灰度发布等高级能力。

参考

迭代记录

  • 2026-06-09:初始化 Nginx 基础与最佳实践指南,整理配置结构、include 插拔式配置、多 server 匹配、location 规则、反向代理、HTTPS、日志、排障和上线检查清单。