一句话速览:Nginx 配置的心智模型只有三层——http 块管全局、server 块管一个站点、location 块管一个 URL 路径。三者嵌套,从外到内逐层缩小范围。90% 的 Nginx 配置问题都能用三句话定位:哪个 server 块匹配了请求?哪个 location 匹配了路径?匹配到的指令做了什么?

目录


技术点 1:配置文件的层级结构——http → server → location

Nginx 配置文件(通常在 /etc/nginx/nginx.conf)是嵌套的块结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 全局配置——工作进程数、日志路径等
worker_processes auto;

events {
worker_connections 1024; # 每个工作进程的最大连接数
}

http {
# http 块:所有站点共享的配置
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf; # 引入其他配置文件(常见做法)

server {
# server 块:一个虚拟主机(一个站点)
listen 80;
server_name example.com;

location / {
# location 块:一个 URL 路径的处理规则
root /var/www/html;
index index.html;
}
}
}

记忆方式:俄罗斯套娃。http 是最外层(全局 HTTP 设置),server 是中间层(一个站点),location 是最内层(一条 URL 路径的规则)。配置项可以在外层定义、内层继承,内层也可以覆盖外层。

1Panel / 宝塔等面板的做法:通常主配置文件 nginx.confinclude /etc/nginx/conf.d/*.conf,每个站点一个 .conf 文件(如 example.com.conf),里面就是一个 server 块。这样加站点只需加文件、不用改主配置。


技术点 2:server 块——虚拟主机,一个 Nginx 托管多个站点

一个 Nginx 可以托管多个站点,靠的是 server_name 区分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 站点 A:博客
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
}

# 站点 B:API
server {
listen 80;
server_name api.example.com;
proxy_pass http://localhost:3000; # 反向代理到后端
}

# 站点 C:默认(所有未匹配的域名)
server {
listen 80 default_server; # 没有其他 server_name 匹配时走这里
server_name _;
return 444; # 直接断开连接
}

关键概念

  • listen:监听端口,80 是 HTTP、443 是 HTTPS
  • server_name:匹配请求的 Host 头。Nginx 收到请求后,按 server_name 找对应的 server 块
  • default_server:没有匹配时的兜底,一个端口只能有一个 default_server
  • 多个域名指向同一个站点:server_name a.com b.com c.com;

技术点 3:location 匹配规则——Nginx 最容易踩坑的地方

location 决定了”这个 URL 路径由什么规则处理”,匹配规则有优先级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
location = /exact {
# 精确匹配(优先级最高),只匹配 /exact 本身
}

location ^~ /priority {
# 前缀匹配(不再做正则匹配),匹配以 /priority 开头的路径
}

location ~ \.php</span> &#123;</span><br><span class="line"> <span class="comment"># 正则匹配(区分大小写),匹配 .php 结尾的路径</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="section">location</span> <span class="regexp">~* \.(jpg|png|css|js) {
# 正则匹配(不区分大小写),匹配静态资源
}

location /api {
# 普通前缀匹配(优先级最低),匹配以 /api 开头的路径
}

优先级从高到低

  1. = 精确匹配(最高)
  2. ^~ 前缀匹配(匹配后不再检查正则)
  3. ~ / ~* 正则匹配(按出现顺序,先匹配到的生效)
  4. 普通前缀匹配(最低,最长前缀优先)

实际例子

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

# 静态资源走正则
location ~* \.(css|js|png|jpg|gif|ico)</span> &#123;</span><br><span class="line"> <span class="attribute">root</span> /var/www/static;</span><br><span class="line"> <span class="attribute">expires</span> <span class="number">30d</span>; <span class="comment"># 浏览器缓存 30 天</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment"># /api 反代到后端</span></span><br><span class="line"> <span class="section">location</span> /api &#123;</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://localhost:3000;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 其他路径找静态文件</span></span><br><span class="line"> <span class="section">location</span> / &#123;</span><br><span class="line"> <span class="attribute">root</span> /var/www/html;</span><br><span class="line"> <span class="attribute">try_files</span> <span class="variable">uri $uri/ /index.html; # SPA 单页应用的标配
}
}

SPA 单页应用的 try_filestry_files $uri uri//index.html的意思是——先找文件uri/ /index.html` 的意思是——先找文件 `uri,再找目录 $uri/,都找不到就返回 index.html(让前端路由处理)。Vue/React 项目部署必配。


技术点 4:反向代理——Nginx 最常见的用途

反向代理就是:用户请求 Nginx → Nginx 转发给后端服务 → 后端返回 → Nginx 转给用户。

1
2
3
4
5
6
7
location /api {
proxy_pass http://localhost:3000; # 转发到本机 3000 端口
proxy_set_header Host host</span>; <span class="comment"># 把原始 Host 传给后端</span></span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">remote_addr; # 传真实客户端 IP
proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Forwarded-Proto <span class="variable">scheme;
}

为什么要传这些 header:后端服务收到的请求是 Nginx 发的,不传的话后端以为请求来自 127.0.0.1,拿不到真实用户 IP 和协议。

带路径前缀的代理

1
2
3
4
5
6
7
8
9
# 访问 https://example.com/app/xxx → 转发到 http://localhost:3000/xxx(去掉 /app 前缀)
location /app/ {
proxy_pass http://localhost:3000/; # 注意末尾的 /,它会把 /app/ 替换掉
}

# 对比:不带末尾 /
location /app/ {
proxy_pass http://localhost:3000; # 不带 /,/app/ 前缀保留,转发为 /app/xxx
}

末尾 / 的区别是最大的坑

  • proxy_pass http://backend/;(带 /)→ /app/foo 变成 /foo
  • proxy_pass http://backend;(不带 /)→ /app/foo 保持 /app/foo

WebSocket 代理(如果后端用了 WS):

1
2
3
4
5
6
7
location /ws {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400s; # WS 长连接不超时
}

技术点 5:静态文件托管与 try_files

最简单的用途——把 HTML/CSS/JS 文件托管出去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server {
listen 80;
server_name example.com;
root /var/www/html; # 文件根目录

# 访问 example.com/ → 找 /var/www/html/index.html
location / {
try_files uri</span><spanclass="variable">uri</span> <span class="variable">uri/ /index.html;
}

# 单独配一个路径
location /study-plan {
# 访问 example.com/study-plan → 找 /var/www/html/study-plan.html
# nginx 自动补 .html 后缀(需要 try_files 配合)
try_files uri</span><spanclass="variable">uri</span> <span class="variable">uri.html $uri/ /index.html;
}
}

root vs alias

  • root /var/www:location 路径拼在 root 后面。location /img + root /var/www → 找 /var/www/img/xxx
  • alias /var/www/images:location 路径被替换。location /img + alias /var/www/images → 找 /var/www/images/xxx

记忆方式:root 是”路径拼接”,alias 是”路径替换”。alias 更直观但要注意末尾 / 要对齐。


技术点 6:负载均衡与 upstream

后端有多个实例时,Nginx 可以分发请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义后端服务器组
upstream backend {
server 127.0.0.1:3000 weight=3; # 权重 3
server 127.0.0.1:3001 weight=1; # 权重 1
server 127.0.0.1:3002 backup; # 备用,前面的都挂了才用
}

server {
listen 80;
location /api {
proxy_pass http://backend; # 直接用 upstream 名字
}
}

负载均衡策略

  • 默认:轮询(round-robin),均匀分配
  • weight=N:按权重分配
  • ip_hash:同一 IP 固定走同一后端(解决 session 问题)
  • least_conn:分配给当前连接数最少的后端
1
2
3
4
5
upstream backend {
ip_hash; # 同一用户固定走同一台
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}

技术点 7:HTTPS 配置与证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server {
listen 443 ssl http2; # HTTPS + HTTP/2
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;
ssl_ciphers HIGH:!aNULL:!MD5;

location / {
root /var/www/html;
}
}

# HTTP 自动跳转 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://host</span><spanclass="variable">host</span><span class="variable">request_uri; # 301 永久重定向到 HTTPS
}

证书来源

  • Let’s Encrypt(免费,用 certbot 自动申请和续期)
  • 阿里云/腾讯云免费 DV 证书
  • 付费证书(OV/EV)

HTTP 跳 HTTPS 的坑:后端服务如果需要知道用户用的是 HTTPS,必须传 X-Forwarded-Proto $scheme,否则后端以为请求是 HTTP 的,生成 HTTP 的重定向链接造成无限重定向。


技术点 8:常用运维命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 测试配置语法是否正确
nginx -t

# 重新加载配置(不中断服务,平滑重启)
nginx -s reload

# 启动 / 停止
nginx # 启动
nginx -s stop # 快速停止
nginx -s quit # 优雅停止(等当前请求处理完)

# Docker 里跑的 Nginx(如 1Panel/OpenResty)
docker exec <容器名> nginx -t # 测试配置
docker exec <容器名> nginx -s reload # 重载配置

# 查看实时日志
tail -f /var/log/nginx/access.log # 访问日志
tail -f /var/log/nginx/error.log # 错误日志

配置变更流程:改配置 → nginx -t 测试 → nginx -s reload 重载。永远先测试再重载,配置语法错误会导致 reload 失败但旧配置仍在跑(不中断服务)。


术语表

术语 含义
http 块 Nginx 配置最外层,管全局 HTTP 设置
server 块 虚拟主机,一个 server 对应一个站点
location 块 URL 路径匹配规则,一个 location 对应一组 URL
root 文件根目录,location 路径拼在后面
alias 路径别名,location 路径被替换
try_files 按顺序尝试文件/目录,找不到用最后一个兜底
proxy_pass 反向代理,把请求转发给后端
upstream 后端服务器组,配合 proxy_pass 做负载均衡
$uri 当前请求的 URI(不含参数)
$host 请求的 Host 头
$remote_addr 客户端 IP
default_server 没有匹配时的兜底 server
499 客户端主动断开(常见于前端超时取消请求)
502 后端服务没起来或端口不对
504 后端响应超时(Gateway Timeout)