02-配置体系与请求匹配

1. 阶段定位

第二阶段是 Nginx 从“会部署、会启动、会看日志”进入“能写正确、稳定、可维护配置”的关键阶段。第一阶段重点是让 Nginx 跑起来,第二阶段重点是回答下面几个问题:

1
2
3
4
5
6
7
8
一个请求进来以后:
-> 先进入哪个 listen 端口
-> 再进入哪个 server
-> 再进入哪个 location
-> URI 是否被改写
-> 静态文件路径如何拼接
-> try_files 是否发生内部跳转
-> 最终返回文件、重定向,还是交给后续代理阶段

很多 Nginx 线上问题并不是因为 Nginx 不稳定,而是因为配置匹配规则理解不清。例如:

  • 请求进入了错误的 server
  • location 优先级判断错误。
  • rootalias 混用导致静态资源 404。
  • try_files 内部跳转不清楚,导致前端路由异常。
  • rewrite lastbreak 混用,导致 URI 改写结果不符合预期。
  • $uri$request_uri 混淆,导致日志、跳转、代理路径判断错误。

本阶段完成后,你应该能做到:

  • 能读懂并设计 Nginx 配置层级结构。
  • 能解释指令在哪个上下文生效。
  • 能判断配置继承和覆盖关系。
  • 能准确判断请求命中哪个 server
  • 能准确判断请求命中哪个 location
  • 能解释 rootaliasindextry_files 的差异。
  • 能正确使用 return 做跳转和固定响应。
  • 能谨慎使用 rewrite,避免滥用和误匹配。
  • 能使用自定义 access log 打印变量,验证请求匹配过程。
  • 能写出结构清晰、可维护、可排查的 Nginx 配置。

2. 学习边界

第二阶段聚焦配置体系和请求匹配,不深入反向代理、负载均衡、HTTPS、安全限流和性能调优。

2.1 本阶段重点

  • 配置上下文:maineventshttpserverlocationupstream
  • 指令作用域、继承和覆盖。
  • listen 与默认 server。
  • server_name 匹配规则。
  • location 匹配规则和优先级。
  • URI、request URI、normalized URI 的区别。
  • rootalias 的路径拼接差异。
  • index 的目录首页行为。
  • try_files 的文件检测和内部跳转。
  • return 的固定响应与跳转。
  • rewrite 的 URI 改写、正则捕获、lastbreak
  • 常用变量:$uri$request_uri$args$host$http_host$remote_addr
  • 自定义日志格式验证匹配过程。

2.2 本阶段暂不深入

下面内容后续阶段再系统学习:

  • proxy_pass 的 URI 拼接规则。
  • upstream 负载均衡算法。
  • 后端超时、重试、连接池。
  • HTTPS、证书、HTTP/2。
  • gzip、缓存、sendfile 深度优化。
  • 限流、限并发、安全防护。
  • 灰度发布、高可用、热升级。
  • Nginx 源码 phase handler 机制。

3. 配置体系总览

Nginx 配置不是简单的键值对文本,而是一套有上下文、作用域、继承关系和执行阶段的配置体系。

典型结构:

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
26
27
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr $host "$request" $status $uri $request_uri';
access_log /var/log/nginx/access.log main;

server {
listen 80 default_server;
server_name _;

location / {
root /usr/share/nginx/html;
index index.html;
}
}
}

可以把 Nginx 配置理解成下面层级:

1
2
3
4
5
main 全局配置
-> events 连接事件配置
-> http HTTP 总配置
-> server 虚拟主机配置
-> location URI 处理配置

3.1 main 上下文

main 是配置文件最外层,不需要显式写出名称。

适合放:

  • 运行用户。
  • worker 进程数。
  • pid 文件。
  • 全局错误日志。
  • 全局 include。
  • 全局资源限制类配置。

示例:

1
2
3
4
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

3.2 events 上下文

events 用于配置事件模型和连接处理能力。

1
2
3
events {
worker_connections 1024;
}

这一阶段只需要知道:它不处理 HTTP 路由,不写 serverlocationroot

3.3 http 上下文

http 是 HTTP 配置的总入口。

适合放:

  • MIME 类型。
  • 默认响应类型。
  • 日志格式。
  • 全局访问日志。
  • gzip、sendfile 等 HTTP 级配置。
  • 多个 server
  • upstream
  • map 等 HTTP 级逻辑。

示例:

1
2
3
4
5
6
7
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;

include /etc/nginx/conf.d/*.conf;
}

3.4 server 上下文

server 表示一个虚拟主机,也可以理解为一个站点入口。

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

location / {
root /data/www/example;
index index.html;
}
}

server 主要解决:

1
这个请求应该进入哪个站点?

判断依据主要是:

  • 监听端口。
  • 监听 IP。
  • Host 头。
  • server_name
  • 默认 server。

3.5 location 上下文

location 用于匹配 URI 路径,决定请求在当前 server 内如何处理。

1
2
3
location /static/ {
root /data/www;
}

location 主要解决:

1
这个请求在当前站点里应该由哪段规则处理?

3.6 upstream 上下文

upstream 用于定义后端服务组,第三阶段反向代理与负载均衡会重点学习。

1
2
3
4
upstream backend_api {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}

本阶段只需要知道:upstream 通常写在 http 中,不能写在 serverlocation 内。

4. 指令上下文与作用域

不同指令只能出现在特定上下文中。写错位置,nginx -t 会报错。

4.1 常见上下文表

指令 常见合法上下文 说明
user main worker 运行用户
worker_processes main worker 进程数量
events main 事件配置块
http main HTTP 配置块
server http 虚拟主机
location server、location URI 匹配配置
upstream http 后端服务组
log_format http 定义日志格式
access_log http、server、location 访问日志
error_log main、http、server、location 错误日志
root http、server、location 静态资源根目录
index http、server、location 默认首页
try_files server、location 文件探测和内部跳转
return server、location、if 返回状态码或重定向
rewrite server、location、if URI 改写

4.2 常见上下文错误

错误示例:把 server 写在最外层。

1
2
3
server {
listen 80;
}

正确示例:server 应该在 http 内。

1
2
3
4
5
http {
server {
listen 80;
}
}

错误示例:把 log_format 写在 server 内。

1
2
3
server {
log_format main '$remote_addr $request';
}

log_format 应写在 http 上下文。

1
2
3
4
5
6
7
http {
log_format main '$remote_addr $request';

server {
access_log /var/log/nginx/access.log main;
}
}

5. 指令继承与覆盖

Nginx 配置存在继承和覆盖关系。很多指令可以在 httpserverlocation 多层配置,子级不配置时使用上级配置,子级配置后覆盖上级。

5.1 access_log 继承示例

1
2
3
4
5
6
7
8
9
10
11
12
http {
access_log /var/log/nginx/access.log main;

server {
listen 80;
server_name example.com;

location / {
root /data/www/example;
}
}
}

这个例子中,serverlocation 没有单独配置 access_log,所以继承 http 级别访问日志。

5.2 server 覆盖 http

1
2
3
4
5
6
7
8
9
10
11
12
13
http {
access_log /var/log/nginx/access.log main;

server {
listen 80;
server_name example.com;
access_log /var/log/nginx/example.access.log main;

location / {
root /data/www/example;
}
}
}

这个例子中,当前 server 的访问日志会写入 /var/log/nginx/example.access.log,而不是全局 access.log。

5.3 location 覆盖 server

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/example.access.log main;

location /download/ {
access_log /var/log/nginx/download.access.log main;
root /data/www;
}
}

访问 /download/file.zip 时,会使用 location 内的日志路径。

5.4 继承不是简单叠加

很多指令不是“追加”,而是“覆盖”。例如子级配置了新的 access_log 后,通常不会同时自动写上级日志,除非你显式配置多个 access_log

学习阶段建议:

  • 全局默认配置放在 http
  • 站点独有配置放在 server
  • 特殊路径配置放在 location
  • 不要在多个层级重复写同一指令,除非明确需要覆盖。

6. 请求匹配总流程

理解 Nginx 配置匹配,必须先建立整体流程。

1
2
3
4
5
6
7
8
9
10
客户端请求
-> 建立 TCP 连接
-> 匹配 listen IP:port
-> 在对应 listen 下选择 server
-> 根据 Host 匹配 server_name
-> 未匹配则进入 default_server
-> 在选定 server 内匹配 location
-> 执行 location 内处理逻辑
-> 可能 return / rewrite / try_files / 静态文件 / 代理
-> 记录 access_log

6.1 示例请求

1
2
GET /static/app.js?version=1 HTTP/1.1
Host: www.example.com

Nginx 会依次判断:

  1. 请求打到哪个本地 IP 和端口。
  2. 该端口下有哪些 server
  3. Host www.example.com 匹配哪个 server_name
  4. URI /static/app.js 匹配哪个 location
  5. 根据 rootaliastry_files 等规则处理。

6.2 两级匹配核心

1
2
server 匹配:解决“进哪个站点”
location 匹配:解决“站点内哪个路径规则处理”

不要把 server_namelocation 混为一谈。server_name 看 Host,location 看 URI。

7. listen 与默认 server

listen 决定 Nginx 在哪个 IP 和端口上接收请求。多个 server 可以监听同一个端口,此时 Nginx 会结合 server_name 和默认 server 决定请求进入哪个 server

7.1 基础 listen 写法

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

表示监听所有可用 IP 的 80 端口。

常见写法:

写法 含义
listen 80; 监听 80 端口,通常绑定所有地址
listen 0.0.0.0:80; 监听所有 IPv4 地址的 80 端口
listen 127.0.0.1:80; 只监听本机回环地址
listen 8080; 监听 8080 端口
listen [::]:80; 监听 IPv6 80 端口
listen 80 default_server; 当前 server 作为该监听地址上的默认 server

7.2 同端口多 server

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name a.example.com;
return 200 'server a\n';
}

server {
listen 80;
server_name b.example.com;
return 200 'server b\n';
}

测试:

1
2
curl -H 'Host: a.example.com' http://127.0.0.1/
curl -H 'Host: b.example.com' http://127.0.0.1/

预期:

1
2
server a
server b

7.3 default_server 的作用

当请求 Host 没有匹配任何 server_name 时,会进入当前监听地址的默认 server。

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80 default_server;
server_name _;
return 444;
}

server {
listen 80;
server_name www.example.com;
return 200 'www.example.com\n';
}

这个配置中:

  • Host 是 www.example.com,进入第二个 server。
  • Host 是 unknown.example.com,进入第一个 default server。
  • 没有 Host 或 Host 异常时,也可能进入 default server。

return 444 是 Nginx 特有状态,表示直接关闭连接,不返回响应。生产中常用它处理未知域名访问。

7.4 没写 default_server 时怎么办

如果同一个 listen 地址上没有显式声明 default_server,Nginx 通常会把配置中第一个出现的 server 作为默认 server。

不推荐依赖这个隐式规则。建议显式配置:

1
2
3
4
5
server {
listen 80 default_server;
server_name _;
return 404;
}

这样配置更清晰,后续新增站点时也不容易误伤。

7.5 IP 维度的默认 server

默认 server 是按监听地址和端口区分的。

1
2
3
4
5
6
7
8
9
10
11
server {
listen 10.0.0.10:80 default_server;
server_name _;
return 200 'default for 10.0.0.10\n';
}

server {
listen 10.0.0.20:80 default_server;
server_name _;
return 200 'default for 10.0.0.20\n';
}

如果一台服务器有多个 IP,不同 IP:port 可以有不同默认 server。

7.6 listen 排查命令

查看配置最终展开:

1
sudo nginx -T | grep -n "listen\|server_name"

查看端口监听:

1
sudo ss -lntp | grep nginx

测试指定 Host:

1
curl -v -H 'Host: www.example.com' http://127.0.0.1/

8. server_name 匹配规则

server_name 用于根据 HTTP Host 头选择虚拟主机。

8.1 基础写法

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

一个 server 可以配置多个名称:

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

8.2 server_name 匹配优先级

Nginx 的 server_name 匹配大致优先级如下:

1
2
3
4
5
1. 精确名称匹配
2. 最长的前缀通配符匹配,例如 *.example.com
3. 最长的后缀通配符匹配,例如 mail.*
4. 第一个匹配的正则 server_name
5. default_server

8.3 精确匹配

1
2
3
4
5
server {
listen 80;
server_name www.example.com;
return 200 'exact www\n';
}

测试:

1
curl -H 'Host: www.example.com' http://127.0.0.1/

精确匹配优先级最高。

8.4 前缀通配符匹配

1
2
3
4
5
server {
listen 80;
server_name *.example.com;
return 200 'wildcard prefix\n';
}

可匹配:

1
2
3
www.example.com
api.example.com
img.example.com

通常不匹配裸域名:

1
example.com

如果裸域名也要匹配,应该显式写出:

1
server_name example.com *.example.com;

8.5 后缀通配符匹配

1
2
3
4
5
server {
listen 80;
server_name mail.*;
return 200 'wildcard suffix\n';
}

可匹配:

1
2
mail.example.com
mail.test.com

这种写法相对少用,可维护性一般,不建议滥用。

8.6 正则匹配

正则 server_name 使用 ~ 开头。

1
2
3
4
5
server {
listen 80;
server_name ~^www\d+\.example\.com$;
return 200 'regex server\n';
}

可匹配:

1
2
www1.example.com
www2.example.com

正则匹配注意事项:

  • 正则匹配优先级低于精确匹配和通配符匹配。
  • 多个正则 server 按配置出现顺序匹配,第一个命中生效。
  • 正则过多会降低可读性和可维护性。
  • 生产环境应优先使用精确名称和清晰通配符。

8.7 server_name _ 的意义

常见配置:

1
2
3
4
5
server {
listen 80 default_server;
server_name _;
return 404;
}

_ 并不是特殊通配符,只是一个普通但通常不会被真实 Host 使用的名称。真正让它成为兜底 server 的是 default_server,不是 _ 本身。

8.8 Host、$host 与 $http_host

Nginx 中常用两个变量观察 Host:

变量 说明
$http_host 原始 Host 请求头,通常保留端口
$host 规范化后的 Host,可能来自请求行、Host 头或 server_name,通常不带端口

示例请求:

1
curl -H 'Host: www.example.com:8080' http://127.0.0.1/

可能看到:

1
2
$http_host = www.example.com:8080
$host = www.example.com

调试 Host 匹配时,建议自定义日志同时打印这两个变量。

9. URI 与请求变量基础

第二阶段最容易混淆的是 URI 相关变量。理解它们对 locationtry_filesrewrite、日志排查都很重要。

9.1 URL、URI、请求参数

请求示例:

1
http://www.example.com/static/app.js?version=1&debug=true

拆开看:

部分 内容
scheme http
host www.example.com
path / URI 路径 /static/app.js
query string version=1&debug=true
request URI /static/app.js?version=1&debug=true

9.2 $request_uri

$request_uri 表示客户端发来的原始 URI,通常包含查询参数。

1
/static/app.js?version=1&debug=true

特点:

  • 保留原始请求中的参数。
  • 通常不会随着内部 URI 规范化而变化。
  • 很适合日志记录和跳转保留原始路径。

9.3 $uri

$uri 表示 Nginx 处理后的规范化 URI,不包含查询参数。

1
/static/app.js

特点:

  • 不包含 query string。
  • 可能被 rewritetry_files 内部跳转改变。
  • 常用于路径匹配、文件查找和内部处理。

9.4 $args 与 $query_string

$args 表示查询参数部分:

1
version=1&debug=true

$query_string 通常等价于 $args

可以通过下面方式返回变量观察:

1
2
3
4
location /debug-uri {
default_type text/plain;
return 200 "uri=$uri\nrequest_uri=$request_uri\nargs=$args\nhost=$host\nhttp_host=$http_host\n";
}

测试:

1
curl -H 'Host: www.example.com:8080' 'http://127.0.0.1/debug-uri?a=1&b=2'

9.5 URI 变量对比表

变量 是否包含参数 是否可能被内部改写影响 典型用途
$request_uri 通常保留原始请求 日志、跳转、保留原始路径
$uri 文件查找、内部跳转、匹配调试
$args 只包含参数 可被修改 参数透传、日志
$is_args 有参数时为 ? 跟随 args 拼接跳转 URL
$host 不包含路径 Host 规范化结果 域名判断、代理头
$http_host 原始 Host 头 原样来自请求头 调试 Host

10. location 匹配规则

location 是第二阶段最重要的知识点之一。它决定 URI 在选定 server 内由哪段配置处理。

10.1 location 常见类型

类型 示例 说明
精确匹配 location = / URI 必须完全相等
前缀匹配 location /static/ URI 以指定前缀开头
优先前缀匹配 location ^~ /static/ 命中后不再检查正则
区分大小写正则 location ~ \.jpg$ 正则匹配,区分大小写
不区分大小写正则 location ~* \.jpg$ 正则匹配,不区分大小写
通用前缀 location / 兜底路径匹配

10.2 location 匹配优先级

核心规则可以记成:

1
2
3
4
5
6
1. 先检查精确匹配 location =
2. 再找最长前缀匹配
3. 如果最长前缀是 ^~,直接使用它,不再检查正则
4. 否则按配置顺序检查正则 location ~ / ~*
5. 如果正则命中,使用第一个命中的正则 location
6. 如果正则都不命中,使用之前找到的最长前缀 location

10.3 示例配置

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
26
27
28
server {
listen 8088;
server_name _;

location = / {
return 200 'A exact /\n';
}

location / {
return 200 'B prefix /\n';
}

location /static/ {
return 200 'C prefix /static/\n';
}

location ^~ /static/images/ {
return 200 'D ^~ /static/images/\n';
}

location ~ \.jpg$ {
return 200 'E regex jpg\n';
}

location ~* \.png$ {
return 200 'F regex png ignore case\n';
}
}

测试结果:

请求 URI 命中 location 原因
/ location = / 精确匹配优先级最高
/abc location / 只有通用前缀匹配
/static/app.js location /static/ 最长前缀,正则不命中
/static/logo.jpg location ~ \.jpg$ 前缀先记录,随后正则命中并覆盖
/static/images/a.jpg location ^~ /static/images/ ^~ 最长前缀命中后跳过正则
/static/a.PNG location ~* \.png$ 不区分大小写正则命中

10.4 精确匹配 location =

1
2
3
4
location = /favicon.ico {
access_log off;
return 204;
}

适合处理:

  • / 首页。
  • /favicon.ico
  • /robots.txt
  • 固定健康检查路径,例如 /health

精确匹配性能和可读性都很好,适合高频固定路径。

10.5 普通前缀匹配

1
2
3
location /static/ {
root /data/www;
}

可匹配:

1
2
3
/static/app.js
/static/css/main.css
/static/images/logo.png

多个普通前缀同时匹配时,使用最长的那个作为候选。

10.6 ^~ 优先前缀匹配

1
2
3
location ^~ /assets/ {
root /data/www;
}

如果 URI 以 /assets/ 开头,且这是最长前缀匹配,Nginx 会直接使用该 location,不再检查正则 location。

适合场景:

  • 静态资源目录明确,不希望被后面的正则劫持。
  • 下载目录明确,需要稳定匹配。
  • 图片、文件、前端资源路径不希望进入动态规则。

10.7 正则 location

1
2
3
location ~ \.php$ {
return 200 'php file\n';
}
1
2
3
location ~* \.(jpg|jpeg|png|gif|webp)$ {
return 200 'image file\n';
}

注意:

  • ~ 区分大小写。
  • ~* 不区分大小写。
  • 多个正则按配置顺序匹配,第一个命中生效。
  • 正则 location 可读性较差,过多会增加维护成本。

10.8 location / 兜底

1
2
3
4
location / {
root /data/www/site;
index index.html;
}

location / 几乎可以匹配所有 URI,常作为兜底规则。建议每个 server 都有清晰的兜底 location,避免请求落入不可预期行为。

10.9 location 排查方法

为了确认请求进入哪个 location,可以临时写 return

1
2
3
location /api/ {
return 200 'hit /api/\n';
}

也可以配置调试日志格式:

1
2
log_format match_debug '$remote_addr host=$host request="$request" uri=$uri request_uri=$request_uri status=$status';
access_log /var/log/nginx/match_debug.log match_debug;

然后用 curl 验证:

1
2
curl -H 'Host: www.example.com' 'http://127.0.0.1/static/app.js?version=1'
sudo tail -f /var/log/nginx/match_debug.log

11. root 与 alias

rootalias 都用于配置静态资源路径,但它们的路径拼接规则完全不同,是 Nginx 新手最容易踩坑的地方之一。

11.1 root 的拼接规则

root 的规则是:

1
真实文件路径 = root 路径 + 完整 URI

示例:

1
2
3
location /static/ {
root /data/www;
}

请求:

1
/static/app.js

实际查找:

1
/data/www/static/app.js

也就是说,location 匹配到的 /static/ 仍然会保留在文件路径中。

11.2 alias 的拼接规则

alias 的规则是:

1
真实文件路径 = alias 路径 + 去掉 location 前缀后的剩余 URI

示例:

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

请求:

1
/static/app.js

实际查找:

1
/data/assets/app.js

这里 /static/ 不会出现在真实文件路径中。

11.3 root 与 alias 对比

配置 请求 URI 实际文件路径
location /static/ { root /data/www; } /static/app.js /data/www/static/app.js
location /static/ { alias /data/assets/; } /static/app.js /data/assets/app.js
location /images/ { root /data/www; } /images/logo.png /data/www/images/logo.png
location /images/ { alias /data/pictures/; } /images/logo.png /data/pictures/logo.png

11.4 alias 末尾斜杠问题

使用 alias 时,强烈建议 locationalias 都保持末尾斜杠一致。

推荐:

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

不推荐:

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

不一致时容易产生难以直观看出的路径拼接问题。

11.5 alias 与正则 location

正则 location 中使用 alias 时通常需要捕获组。

1
2
3
location ~ ^/download/(.*)$ {
alias /data/files/$1;
}

请求:

1
/download/a/b/report.pdf

实际文件:

1
/data/files/a/b/report.pdf

这种配置可读性较差,生产中应谨慎使用。能用清晰前缀匹配解决时,不要优先使用正则 alias。

11.6 root 与 alias 选择建议

优先使用 root 的场景:

  • URI 路径和磁盘路径结构一致。
  • /static/ 对应磁盘 /data/www/static/
  • 配置简单、直观、团队容易理解。

适合使用 alias 的场景:

  • URI 路径和磁盘路径结构不一致。
  • /static/ 需要映射到 /data/assets/
  • 某个 URL 前缀只是对外暴露路径,不希望出现在磁盘路径中。

建议:

1
2
能用 root 清晰表达,就优先 root。
需要 URL 前缀映射到完全不同目录,再用 alias。

12. index 指令

index 用于访问目录 URI 时,指定默认首页文件。

12.1 基础示例

1
2
3
4
location / {
root /data/www/site;
index index.html index.htm;
}

请求:

1
/

Nginx 会尝试查找:

1
2
/data/www/site/index.html
/data/www/site/index.htm

12.2 访问目录与访问文件

如果请求:

1
/about/

且配置:

1
2
3
4
location / {
root /data/www/site;
index index.html;
}

Nginx 会尝试查找:

1
/data/www/site/about/index.html

如果请求:

1
/about.html

则会直接查找:

1
/data/www/site/about.html

12.3 没有 index 时的 403

如果访问的是目录,并且目录下没有 index 指定的文件,通常会返回 403 Forbidden,错误日志中可能出现:

1
directory index of "/data/www/site/" is forbidden

解决方式:

  • 补充 index.html
  • 修改 index 指令。
  • 如果确实需要列目录,开启 autoindex on;

学习阶段了解 autoindex 即可,不建议生产随意开启目录列表。

13. try_files 执行逻辑

try_files 用于按顺序检查文件或目录是否存在,命中则使用该路径,全部不命中则跳转到最后一个参数。

13.1 基础语法

1
2
try_files file ... uri;
try_files file ... =code;

常见写法:

1
2
3
4
location / {
root /data/www/site;
try_files $uri $uri/ /index.html;
}

执行逻辑:

1
2
3
1. 检查 /data/www/site + $uri 是否存在
2. 如果不存在,检查 /data/www/site + $uri/ 是否存在目录
3. 如果还不存在,内部跳转到 /index.html

13.2 静态站点常用配置

1
2
3
4
5
6
7
8
9
10
server {
listen 8088;
server_name _;
root /data/www/site;
index index.html;

location / {
try_files $uri $uri/ =404;
}
}

适合普通静态站点:

  • 文件存在则返回文件。
  • 目录存在则尝试目录首页。
  • 都不存在则返回 404。

13.3 前端 history 路由配置

Vue、React、Angular 等前端单页应用使用 history 路由时,刷新 /user/profile 可能需要回退到 /index.html

1
2
3
4
5
6
7
8
9
10
server {
listen 8088;
server_name _;
root /data/www/frontend;
index index.html;

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

请求 /user/profile

1
2
3
1. 查找 /data/www/frontend/user/profile
2. 查找 /data/www/frontend/user/profile/
3. 都不存在,内部跳转到 /index.html

浏览器仍然显示 /user/profile,但 Nginx 返回的是前端入口文件 index.html

13.4 try_files 最后一个参数

最后一个参数可以是 URI:

1
try_files $uri $uri/ /index.html;

也可以是状态码:

1
try_files $uri $uri/ =404;

也可以跳转到命名 location:

1
2
3
4
5
6
7
location / {
try_files $uri @fallback;
}

location @fallback {
return 404;
}

命名 location 以 @ 开头,只能内部跳转,不能被客户端直接请求。

13.5 try_files 与 root 的关系

try_files 检查文件时,会结合当前上下文中的 rootalias

1
2
3
4
5
root /data/www/site;

location /static/ {
try_files $uri =404;
}

请求:

1
/static/app.js

检查:

1
/data/www/site/static/app.js

13.6 try_files 常见误区

误区一:以为 try_files 会改变浏览器地址。

实际:try_files/index.html 是内部跳转,不会改变浏览器地址栏。

误区二:忘记配置 root

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

如果当前上下文没有正确 root,文件检查路径可能不是你预期的路径。

误区三:前端项目把所有请求都回退到 index.html,导致真实 404 文件也返回 200。

例如 /not-exist.png 也返回 index.html,这对静态资源不友好。更稳妥的做法是静态资源单独配置:

1
2
3
4
5
6
7
location ^~ /assets/ {
try_files $uri =404;
}

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

14. return 指令

return 用于直接返回状态码、文本或重定向。它比 rewrite 更直接、更清晰,能用 return 解决的跳转,不要优先使用 rewrite

14.1 返回固定状态码

1
2
3
location /health {
return 200 'ok\n';
}

测试:

1
curl -i http://127.0.0.1/health

14.2 返回 404

1
2
3
4
5
server {
listen 80 default_server;
server_name _;
return 404;
}

用于未知域名兜底。

14.3 返回 301 永久重定向

1
2
3
4
5
server {
listen 80;
server_name example.com;
return 301 http://www.example.com$request_uri;
}

含义:访问 example.com 时,永久跳转到 www.example.com,并保留原始路径和参数。

请求:

1
http://example.com/a/b?x=1

跳转到:

1
http://www.example.com/a/b?x=1

14.4 返回 302 临时重定向

1
2
3
location /old-page {
return 302 /new-page;
}

302 适合临时跳转,301 适合永久规范化跳转。

14.5 return 与 rewrite 的选择

优先选择 return 的场景:

  • 域名跳转。
  • HTTP 到 HTTPS 跳转。
  • 固定旧路径跳转新路径。
  • 健康检查返回文本。
  • 默认 server 返回 404 或 444。

return 的优点:

  • 执行直接。
  • 可读性强。
  • 不依赖复杂正则。
  • 不容易产生内部跳转副作用。

15. rewrite 指令

rewrite 用于基于正则改写 URI。它很强大,但也容易被滥用。第二阶段要掌握它的基本行为,更要知道什么时候不该用。

15.1 基础语法

1
rewrite regex replacement [flag];

示例:

1
2
3
location / {
rewrite ^/old/(.*)$ /new/$1 last;
}

请求:

1
/old/a.html

改写为:

1
/new/a.html

15.2 常见 flag

flag 说明
last 停止当前 rewrite,使用新 URI 重新进行 location 匹配
break 停止当前 rewrite,在当前 location 继续处理
redirect 返回 302 临时重定向
permanent 返回 301 永久重定向

15.3 last 示例

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 8088;
root /data/www/site;

location /old/ {
rewrite ^/old/(.*)$ /new/$1 last;
}

location /new/ {
return 200 'hit new location: $uri\n';
}
}

请求:

1
curl http://127.0.0.1:8088/old/a

流程:

1
2
3
4
1. 先命中 location /old/
2. rewrite 把 URI 改成 /new/a
3. last 触发重新匹配 location
4. 命中 location /new/

15.4 break 示例

1
2
3
4
location /old/ {
rewrite ^/old/(.*)$ /new/$1 break;
return 200 'uri=$uri\n';
}

break 不会重新发起 location 匹配,而是在当前 location 内继续处理。

15.5 permanent 与 redirect

1
rewrite ^/old-page$ /new-page permanent;

等价于更推荐的:

1
2
3
location = /old-page {
return 301 /new-page;
}

如果只是跳转,优先使用 return

15.6 rewrite 与参数

默认情况下,replacement 中不写 ? 时,原始参数可能会被保留。

1
rewrite ^/old$ /new last;

请求:

1
/old?a=1

可能变成:

1
/new?a=1

如果想丢弃原参数,可以在 replacement 末尾加 ?

1
rewrite ^/old$ /new? last;

这类细节容易引发线上问题,因此跳转类需求建议优先用 return 显式拼接。

15.7 rewrite 常见误区

  • 用 rewrite 做所有跳转,导致配置难读。
  • 不理解 last 会重新匹配 location。
  • 不理解 break 仍留在当前 location。
  • 正则写得过宽,误伤其他 URI。
  • 忘记参数是否保留。
  • 多条 rewrite 连续执行,最终 URI 难以判断。

建议:

1
2
3
能用 return,就不用 rewrite。
能用 location 精确匹配,就不用复杂正则。
必须 rewrite 时,写清楚注释和测试用例。

16. Nginx 常用变量

变量是理解 Nginx 请求处理过程的重要工具。第二阶段不要求掌握所有变量,但必须熟悉常用于匹配、日志、跳转和排查的变量。

16.1 请求与 URI 变量

变量 示例 说明
$request GET /a?x=1 HTTP/1.1 完整请求行
$request_method GET 请求方法
$request_uri /a?x=1 原始请求 URI,包含参数
$uri /a 规范化 URI,不包含参数,可能被内部改写
$document_uri /a 通常等同于 $uri
$args x=1 查询字符串
$query_string x=1 通常等同于 $args
$is_args ? 或空 有参数时为 ?,无参数为空

常见拼接:

1
return 301 https://www.example.com$uri$is_args$args;

如果只是保留用户原始路径和参数,通常更简单:

1
return 301 https://www.example.com$request_uri;

16.2 Host 与客户端变量

变量 说明
$host 规范化 Host,不一定完全等于原始请求头
$http_host 原始 Host 请求头
$server_name 当前匹配 server 的 server_name
$server_addr 服务端地址
$server_port 服务端端口
$remote_addr 客户端 IP
$remote_port 客户端端口
$server_protocol 协议版本,例如 HTTP/1.1

注意:如果 Nginx 前面还有负载均衡或 CDN,$remote_addr 可能是上一层代理 IP,而不是真实用户 IP。真实客户端 IP 的处理后续阶段再学习。

16.3 请求头变量

Nginx 会把请求头转换成 $http_头名 变量:

1
2
3
4
User-Agent        -> $http_user_agent
Referer -> $http_referer
X-Forwarded-For -> $http_x_forwarded_for
X-Real-IP -> $http_x_real_ip

示例:

1
log_format header_debug '$remote_addr host=$host ua="$http_user_agent" xff="$http_x_forwarded_for"';

16.4 响应与日志变量

变量 说明
$status HTTP 响应状态码
$body_bytes_sent 响应体字节数
$bytes_sent 总发送字节数
$request_time 请求总耗时,单位秒
$time_local 本地时间格式
$msec 秒级时间戳,带毫秒

第二阶段重点用它们辅助判断匹配结果,不急着做性能分析。

16.5 变量调试 location

可以临时增加一个调试接口:

1
2
3
4
location = /debug-vars {
default_type text/plain;
return 200 "request=$request\nuri=$uri\nrequest_uri=$request_uri\nargs=$args\nhost=$host\nhttp_host=$http_host\nserver_name=$server_name\nremote_addr=$remote_addr\n";
}

测试:

1
curl -H 'Host: www.example.com:8088' 'http://127.0.0.1:8088/debug-vars?a=1&b=2'

17. 自定义日志验证匹配过程

复杂配置不要靠猜,应该让日志帮助你确认请求进入了哪个 server、哪个 location,以及 URI 是否发生变化。

17.1 定义调试日志格式

http 中定义:

1
2
3
4
log_format match_debug 'time=$time_local remote=$remote_addr '
'host=$host http_host=$http_host server_name=$server_name '
'request="$request" uri=$uri request_uri=$request_uri args="$args" '
'status=$status bytes=$body_bytes_sent request_time=$request_time';

server 中启用:

1
access_log /var/log/nginx/match_debug.log match_debug;

17.2 使用响应头标记 location

有时可以在不同 location 加响应头,辅助浏览器或 curl 判断命中规则。

1
2
3
4
5
6
7
8
9
location /static/ {
add_header X-Matched-Location '/static/' always;
root /data/www;
}

location / {
add_header X-Matched-Location '/' always;
root /data/www/site;
}

测试:

1
curl -I http://127.0.0.1:8088/static/app.js

观察:

1
X-Matched-Location: /static/

17.3 使用 return 做最小验证

当你不确定匹配顺序时,最简单的验证方式就是临时 return

1
2
3
4
5
6
7
location ^~ /assets/ {
return 200 'hit ^~ /assets/\n';
}

location ~ \.js$ {
return 200 'hit regex js\n';
}

测试:

1
curl http://127.0.0.1:8088/assets/app.js

如果返回 hit ^~ /assets/,说明 ^~ 阻止了正则继续匹配。

18. 多 server_name 实验

18.1 实验目标

验证同端口多个 server 时,Host 如何决定请求进入哪个虚拟主机。

18.2 实验配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 8088 default_server;
server_name _;
return 200 'default server\n';
}

server {
listen 8088;
server_name a.test.local;
return 200 'server a\n';
}

server {
listen 8088;
server_name b.test.local;
return 200 'server b\n';
}

server {
listen 8088;
server_name *.wild.test.local;
return 200 'wildcard server\n';
}

18.3 验证命令

1
2
3
4
5
6
7
sudo nginx -t
sudo systemctl reload nginx

curl -H 'Host: a.test.local' http://127.0.0.1:8088/
curl -H 'Host: b.test.local' http://127.0.0.1:8088/
curl -H 'Host: api.wild.test.local' http://127.0.0.1:8088/
curl -H 'Host: unknown.local' http://127.0.0.1:8088/

18.4 预期结果

1
2
3
4
server a
server b
wildcard server
default server

18.5 实验结论

  • listen 先确定端口范围。
  • server_name 再根据 Host 匹配 server。
  • 未匹配任何 server_name 时进入 default_server
  • _ 本身不是通配符,兜底能力来自 default_server

19. location 优先级实验

19.1 实验目标

通过实际请求验证 location =、普通前缀、^~~~* 的优先级。

19.2 实验配置

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
26
27
28
server {
listen 8089;
server_name _;

location = / {
return 200 'exact root\n';
}

location / {
return 200 'prefix root\n';
}

location /static/ {
return 200 'prefix static\n';
}

location ^~ /static/images/ {
return 200 'priority prefix images\n';
}

location ~ \.jpg$ {
return 200 'regex jpg\n';
}

location ~* \.png$ {
return 200 'regex png ignore case\n';
}
}

19.3 验证命令

1
2
3
4
5
6
curl http://127.0.0.1:8089/
curl http://127.0.0.1:8089/abc
curl http://127.0.0.1:8089/static/app.js
curl http://127.0.0.1:8089/static/logo.jpg
curl http://127.0.0.1:8089/static/images/a.jpg
curl http://127.0.0.1:8089/static/a.PNG

19.4 结论表

请求 预期返回 说明
/ exact root 精确匹配最高
/abc prefix root 兜底前缀
/static/app.js prefix static 最长前缀且无正则命中
/static/logo.jpg regex jpg 正则覆盖普通前缀
/static/images/a.jpg priority prefix images ^~ 跳过正则
/static/a.PNG regex png ignore case ~* 不区分大小写

20. root 与 alias 实验

20.1 实验目标

通过真实文件访问验证 rootalias 的路径拼接差异。

20.2 准备文件

1
2
3
4
5
6
sudo mkdir -p /data/www/static
sudo mkdir -p /data/assets

echo 'from root static' | sudo tee /data/www/static/root.txt
echo 'from alias assets' | sudo tee /data/assets/alias.txt
sudo chmod -R 755 /data/www /data/assets

20.3 实验配置

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 8090;
server_name _;

location /static-root/ {
root /data/www;
}

location /static-alias/ {
alias /data/assets/;
}
}

20.4 关键现象

请求:

1
curl http://127.0.0.1:8090/static-root/root.txt

Nginx 查找:

1
/data/www/static-root/root.txt

这通常会 404,因为真实文件在:

1
/data/www/static/root.txt

如果要用 root,URI 前缀要和磁盘目录一致:

1
2
3
location /static/ {
root /data/www;
}

请求:

1
curl http://127.0.0.1:8090/static-alias/alias.txt

Nginx 查找:

1
/data/assets/alias.txt

这就是 alias 的前缀替换效果。

21. try_files 实验

21.1 普通静态站点

1
2
3
4
5
6
7
8
9
10
server {
listen 8091;
server_name _;
root /data/www/site;
index index.html;

location / {
try_files $uri $uri/ =404;
}
}

准备文件:

1
2
3
4
sudo mkdir -p /data/www/site/about
printf 'home\n' | sudo tee /data/www/site/index.html
printf 'about\n' | sudo tee /data/www/site/about/index.html
printf 'app\n' | sudo tee /data/www/site/app.js

测试:

1
2
3
4
curl http://127.0.0.1:8091/
curl http://127.0.0.1:8091/about/
curl http://127.0.0.1:8091/app.js
curl -I http://127.0.0.1:8091/not-exist

21.2 前端 history 路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 8092;
server_name _;
root /data/www/frontend;
index index.html;

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

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

这个配置的好处:

  • /assets/app.js 必须真实存在,不存在就 404。
  • /user/profile 不存在时回退到 /index.html
  • 前端 history 路由刷新不再由 Nginx 返回 404。

22. rewrite 与 return 实验

22.1 return 域名跳转

1
2
3
4
5
server {
listen 8093;
server_name old.example.com;
return 301 http://new.example.com$request_uri;
}

测试:

1
curl -I -H 'Host: old.example.com' 'http://127.0.0.1:8093/a/b?x=1'

观察 Location

1
Location: http://new.example.com/a/b?x=1

22.2 rewrite last

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 8094;
server_name _;

location /old/ {
rewrite ^/old/(.*)$ /new/$1 last;
}

location /new/ {
return 200 'new uri=$uri request_uri=$request_uri\n';
}
}

测试:

1
curl 'http://127.0.0.1:8094/old/test?a=1'

重点观察:

  • $uri 可能变成 /new/test
  • $request_uri 仍能体现原始请求 /old/test?a=1
  • last 会重新匹配 location。

22.3 rewrite break

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 8095;
server_name _;

location /old/ {
rewrite ^/old/(.*)$ /new/$1 break;
return 200 'still in old location, uri=$uri\n';
}

location /new/ {
return 200 'new location\n';
}
}

测试:

1
curl http://127.0.0.1:8095/old/test

预期仍在 /old/ location 内处理,不会重新进入 /new/ location。

23. 可维护配置实践

23.1 推荐目录拆分

1
2
3
4
5
6
7
/etc/nginx/nginx.conf
/etc/nginx/conf.d/00-default.conf
/etc/nginx/conf.d/site-blog.conf
/etc/nginx/conf.d/site-admin.conf
/etc/nginx/conf.d/site-api.conf
/etc/nginx/snippets/security-headers.conf
/etc/nginx/snippets/static-cache.conf

建议:

  • nginx.conf 只放全局通用配置。
  • conf.d/*.conf 每个文件对应一个站点或一类入口。
  • 可复用片段放在 snippets
  • 文件名用数字或业务名控制顺序和含义。
  • 默认 server 显式命名为 00-default.conf

23.2 推荐 server 结构

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

access_log /var/log/nginx/www.example.com.access.log main;
error_log /var/log/nginx/www.example.com.error.log warn;

root /data/www/www.example.com;
index index.html;

location = /health {
access_log off;
return 200 'ok\n';
}

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

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

23.3 配置书写原则

  • 先写默认 server,再写业务 server。
  • 先写精确 location,再写特殊前缀,再写兜底 /
  • 静态资源路径优先使用 ^~ 避免被正则误伤。
  • 能用 return 就不用 rewrite
  • 能用清晰前缀匹配就不用正则。
  • rootalias 不要混淆,使用 alias 时保持末尾斜杠一致。
  • 每个站点配置独立 access log 和 error log,便于排查。
  • 每次修改配置后执行 nginx -t,再 reload。

24. 常见误区与反例

24.1 误以为 server_name _ 是通配符

错误理解:

1
server_name _ 可以匹配所有域名

正确理解:

1
_ 只是普通名称,兜底效果来自 default_server

24.2 误以为最长前缀一定最终生效

普通最长前缀只是候选。如果后续正则 location 命中,正则会覆盖普通前缀。除非最长前缀使用 ^~

24.3 root 与 alias 混用

错误:

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

建议:

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

24.4 前端 history 路由吞掉静态资源 404

问题配置:

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

如果所有资源都走这个规则,不存在的 JS/CSS/图片也可能返回 index.html。建议静态资源单独配置:

1
2
3
4
5
6
7
location ^~ /assets/ {
try_files $uri =404;
}

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

24.5 rewrite 过度使用

不推荐:

1
rewrite ^/(.*)$ https://www.example.com/$1 permanent;

推荐:

1
return 301 https://www.example.com$request_uri;

25. 第二阶段必做实验清单

  1. 配置多个 server_name,验证 Host 匹配规则。
  2. 配置 default_server,验证未知 Host 进入默认 server。
  3. 配置精确、前缀、^~、正则 location,验证完整优先级。
  4. 对比 rootalias 在不同 URI 下的文件路径拼接结果。
  5. 使用 try_files $uri $uri/ =404 配置普通静态站点。
  6. 使用 try_files $uri $uri/ /index.html 支持前端 history 路由。
  7. 使用 return 301 做域名规范化跳转。
  8. 使用 rewrite last 做 URI 内部改写并观察重新匹配。
  9. 使用 rewrite break 验证停留在当前 location 的行为。
  10. 配置自定义 access log,打印 $uri$request_uri$host$http_host
  11. 故意制造 location 误匹配,并通过日志和 return 修复。
  12. 故意制造 root/alias 404,并通过 error log 解释真实文件路径。

26. 第二阶段命令速查表

命令 作用
sudo nginx -t 检查配置语法
sudo nginx -T 输出完整配置,包含 include 后内容
sudo systemctl reload nginx 平滑加载新配置
`sudo ss -lntp grep nginx`
curl -H 'Host: a.test.local' http://127.0.0.1:8088/ 指定 Host 测试 server 匹配
curl -I http://127.0.0.1:8088/path 查看响应头和跳转
curl -v http://127.0.0.1:8088/path 查看详细请求响应过程
sudo tail -f /var/log/nginx/access.log 实时查看访问日志
sudo tail -f /var/log/nginx/error.log 实时查看错误日志
`sudo nginx -T grep -n “server_name|location|root|alias”`

27. 第二阶段知识检查

27.1 配置体系

  1. maineventshttpserverlocation 的职责分别是什么?
  2. 为什么 server 不能写在最外层?
  3. log_format 应该写在哪个上下文?
  4. access_loghttpserverlocation 中如何继承和覆盖?
  5. 为什么建议一个站点一个配置文件?

27.2 server 匹配

  1. listen 80 default_server 的作用是什么?
  2. 没有显式配置 default_server 时,未知 Host 会进入哪里?
  3. server_name _ 是否是通配符?
  4. server_name 精确匹配、通配符、正则的优先级是什么?
  5. $host$http_host 有什么区别?

27.3 location 匹配

  1. location = / 的优先级是什么?
  2. 普通前缀和正则 location 谁最终生效?
  3. ^~ 的作用是什么?
  4. 多个正则 location 都能匹配时,哪个生效?
  5. 为什么建议静态资源目录使用 ^~

27.4 静态路径

  1. root 的路径拼接规则是什么?
  2. alias 的路径拼接规则是什么?
  3. alias 为什么建议末尾带 /
  4. 访问目录没有 index 文件时为什么常见 403?
  5. 如何通过 error log 判断真实文件路径?

27.5 try_files 与 rewrite

  1. try_files $uri $uri/ /index.html 的执行顺序是什么?
  2. try_files 的最后一个参数可以是什么?
  3. return 301rewrite ... permanent 更推荐哪个?为什么?
  4. rewrite lastrewrite break 有什么区别?
  5. $uri$request_uri 在 rewrite 后可能有什么差异?

28. 第二阶段过关标准

完成本阶段后,你应该达到下面标准:

  • 能手写一个多域名、多 location 的 Nginx 配置。
  • 能准确解释一次请求最终进入哪个 server 和哪个 location
  • 能说明 listendefault_serverserver_name 的关系。
  • 能说明 location =^~~~*、普通前缀的优先级。
  • 能准确解释 rootalias 的文件路径拼接差异。
  • 能使用 try_files 支持普通静态站点和前端 history 路由。
  • 能优先使用 return 做清晰跳转,避免 rewrite 滥用。
  • 能解释 rewrite lastbreak 的差异。
  • 能通过自定义 access log 打印变量,验证匹配和改写是否符合预期。
  • 能根据 nginx -T、access log、error log 排查配置匹配问题。

29. 推荐练习顺序

建议按下面顺序练习:

1
2
3
4
5
6
7
8
9
10
11
12
13
配置上下文
-> 指令继承与覆盖
-> listen/default_server
-> server_name 精确与通配符匹配
-> location 优先级
-> root 与 alias
-> index 与目录访问
-> try_files 静态站点
-> try_files 前端 history 路由
-> return 跳转
-> rewrite last/break
-> 自定义日志打印变量
-> 综合排障

不要一开始就写复杂正则和 rewrite。先把 server、location、root、alias、try_files 这些基础规则练熟。

30. 阶段总结

第二阶段的核心是“请求到底命中了哪段配置”。真正掌握本阶段后,你应该能清楚说明:

1
2
3
4
5
6
7
8
9
10
11
请求进入哪个 listen
请求进入哪个 server
Host 如何匹配 server_name
未知 Host 如何进入 default_server
URI 如何匹配 location
location 优先级如何判断
root 和 alias 如何生成真实文件路径
try_files 如何按顺序检查并内部跳转
return 为什么比 rewrite 更适合简单跳转
rewrite last 和 break 为什么行为不同
如何通过日志变量验证判断结果

完成本阶段后,再进入第三阶段“反向代理与负载均衡”。那时你已经能稳定判断请求入口和路径匹配,再学习 proxy_passupstream、超时、重试和 Header 传递就会更清晰。