nginx 配置长跑(上):从一份 server 到看懂整套路由规则

关键词:nginx / 配置心智模型 / http-server-location / 路由优先级 / location / rewrite / 反向代理

引言:从「改一行配置」到「线上整站 502」

有一次,一个同事说要「顺手改下 nginx 配置」,给某个接口加一条简单的转发规则。

几分钟后,测试环境一片 502,登录页面也打不开了,大家第一反应是:是不是后端挂了、是不是 Redis 崩了。

最后排查下来,问题既不是网络,也不是代码,而是:

  • server 里多加了一条 location
  • 少看了几行已有规则;
  • 让一个优先级更高的 location 把全站请求都「截胡」了。

nginx 配置最大的坑,不是你不会写,而是你以为你写的是这条路,其实它走的是另一条。

这一篇,我们就不从「指令大字典」开始,而是从一份最常见的 server 配置入手,把 nginx 的基本心智模型和路由优先级讲清楚。


一、先把大图看清:http / server / location 到底在干嘛?

绝大部分人第一次看到 nginx 配置,大概是这样的结构:

nginx 复制代码
http {
    include       mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  localhost;
        charset      utf-8;

        location / {
            root   html;
            index  index.html index.htm;
        }

        location /test {
            proxy_pass https://wangmiaozero.cn;
        }
    }
}

如果只记这一句就够用了:

http 是「总配置」,server 是「虚拟主机」,location 是「这一台主机上的路由规则」。

1. http:全局规则与公共基础设施

http { ... } 里通常放的是「所有 HTTP 服务都能用到的东西」:

  • include mime.types:引入文件类型映射;
  • default_type:默认响应类型;
  • 各种全局的缓存、日志、连接数、超时配置等。

你可以把 http 想象成「一个机房」:

  • 机房里会有一堆机器(server);
  • 机房级别会有统一的规矩(http 里的配置);
  • 单个机器也可以在自己房间里加点特殊设置(server 级别覆盖)。

2. server:一台逻辑上的「虚拟主机」

server { ... } 就是一台虚拟主机,它通过:

  • listen:监听端口;
  • server_name:匹配域名;

来决定「这个请求是不是该我接」。

一个 nginx.conf 里可以有很多个 server

  • server_name www.xxx.com:网站 A;
  • server_name api.xxx.com:网站 B;
  • server_name *.internal.xxx.com:内部管理系统等。

用户的 HTTP 请求,先是被某一个 server 接住,才会往 location 里面走。

3. location:这一台主机上的「路由与分发规则」

有了 server 之后,剩下的问题就是:

收到一个请求路径 /foo/bar,到底交给谁处理?

这就是 location 的工作了。

常见的指令大致长这样:

  • root:静态资源根目录;
  • index:默认首页文件;
  • proxy_pass:把请求转发到后端;
  • rewrite:在当前域名下改写路径。

日常开发中,大部分「玄学问题」都出在 location ------

要么没命中预期的规则,要么被更高优先级的规则截胡。


二、location 的 7 种写法,其实只是在玩「谁先说了算」

网上经常会看到这样的总结:

= 精确匹配、^~ 前缀、~ 正则、/ 通吃」,记完一遍下次又忘了。

我们换个更工程一点的视角:location 玩的是「优先级 + 是否继续匹配」这两个维度

1. 先看 7 种写法

按照官方语义 + 实战行为,location 粗略可以分成 7 类,从高到低是:

  1. = 精确匹配
  2. 完整路径匹配(如 location /test/aaa/bbb.html
  3. ^~ /test:以 /test 开头,命中后不再往后找
  4. ~ pattern:区分大小写的正则,命中后不再往后找
  5. ~* pattern:不区分大小写的正则,命中后不再往后找
  6. /test:普通前缀匹配,命中后还会继续往后找更长的前缀
  7. /:兜底规则,什么都匹配,优先级最低

你可以记成两个简单的问题:

  1. 当前规则优先级高不高?
  2. 命中之后还要不要继续往后找?

2. 两条大原则 + 两条细则

如果你只想背少量规则,把这 4 条刻进脑子就够了:

  • 整体优先级:
    精确匹配(=) > 完整路径 > ^~ > 正则(~ / ~*) > 普通前缀(/xxx) > /

  • 是否继续匹配:

    除了普通前缀 /xxx 会继续往后找,其它类型(= / ^~ / ~ / ~*)一旦命中就停止。

  • 同类型之间:

    • 多个正则之间 / 多个 ^~ 之间:命中第一个就停,顺序很重要;
    • 多个普通前缀(都是 /xxx 开头):最长匹配优先,跟先后顺序无关。
  • / 永远是兜底:

    不管前面写了多少规则,/ 都是最后一条救命稻草。

如果觉得记不住,可以把 location 理解成:
「先挑最有资格说话的人,然后看他有没有把话说死」。


三、通过 5 个小实验,把优先级打进肌肉记忆

理解规则是一回事,能不能在脑子里「算出结果」是另一回事。

下面的例子全部可以直接丢进 nginx 里测试。

示例一:精确匹配永远优先于普通 /path

nginx 复制代码
location /hello.json {
    default_type  text/html;
    return 200 '111';
}

location = /hello.json {
    default_type  text/html;
    return 200 '222';
}

访问 /hello.json,返回的是 222

  • 原因:= 精确匹配优先级最高,先命中就直接停;
  • 普通 /hello.json 虽然看起来一样,但在规则里属于「前缀匹配」。

示例二:^~ 比正则更「强势」

nginx 复制代码
# 虽然 222 在后面,但是由于 ^~ 优先级更高,所以第二个生效
location ~ /test/aaa {
    return 200 '111';
}

location ^~ /test/aaa {
    return 200 '222';
}

访问 /test/aaa/bbb.json,返回的是 222

解释:

  • ^~ /test/aaa 属于「前缀但不再继续找」这一类,优先级高于正则;
  • 即便它在后面出现,依然会先拿到话语权。

示例三:多个正则,先写的先赢

nginx 复制代码
# 多个正则之间,第一个匹配生效,与顺序有关
# 虽然越往后匹配越精确,但只要是正则,匹配到第一个就停止
location ~ / {
    return 200 '111';
}

location ~ /test/aaa {
    return 200 '222';
}

location ~ /test/aaa/.*\.(gif|jpg|jpeg)$ {
    return 200 '333';
}

访问 /test/aaa/bbb.json,命中的其实是第一条正则(返回 111),因为:

  • / 这个正则也能匹配一切路径;
  • 正则之间遵循「先写先赢」,匹配到第一个就不再往后看。

示例四:同路径前缀下,正则 > 普通前缀

nginx 复制代码
# 不管二者如何交换顺序,始终都是第二个生效
location /test/aaa {
    return 200 '111';
}

location ~ /test/aaa {
    return 200 '222';
}

访问 /test/aaa/bbb.json,返回 222

  • ~ /test/aaa 是正则,优先级比普通前缀 /test/aaa 更高;
  • 先比「种类优先级」,再谈顺序。

示例五:都是前缀匹配时,看谁更长

nginx 复制代码
# 都是 / 开头时,最长匹配生效,和先后顺序无关
location /test/ {
    return 200 '111';
}

location /test/aaa/ccc {
    return 200 '222';
}

location /test/aaa/ {
    return 200 '333';
}

location / {
    return 200 '444';
}

访问 /test/aaa/bbb/ccc.json

  • /test/ 能匹配;
  • /test/aaa/ 也能匹配;
  • /test/aaa/ccc 也能匹配;

最终选的是字符串最长 的那条:/test/aaa/ccc → 返回 222


四、rewrite 和 proxy_pass:重写 vs 转发,不要搞混

日常配置里,大家最容易把 rewriteproxy_pass 混成一团:

「反正都是改 URL,对吧?」------其实差别挺大。

1. rewrite:只在当前域名里改路由

rewrite 的典型用途是:

  • 同一个域名 下,把 /old/path 改写成 /new/path
  • 决定是内部跳转(last)还是返回 301/302(redirect / permanent)。

它做的只是路径层面的改写,不会帮你换服务器、换端口:

nginx 复制代码
location /old {
    rewrite ^/old/(.*)$ /new/$1 last;
}

2. proxy_pass:把请求「交给别人处理」

proxy_pass 则是反向代理:

  • 可以把请求丢给任意地址:http://127.0.0.1:8080https://api.xxx.com
  • 可以在这一层加各种 header、超时、缓冲配置等。
nginx 复制代码
location /api {
    proxy_set_header X-Real-IP        $remote_addr;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_set_header Host             $http_host;
    proxy_connect_timeout 10;
    proxy_pass http://127.0.0.1:6666;
}

一句话区分:

  • rewrite:路由改写,只在当前主机内「换一条路走」;
  • proxy_pass:反向代理,把整条请求「交给另一台服务」。

五、nginx 配置的「可预期性」:从记语法到记行为

很多人觉得 nginx 配置玄学,本质上是因为:

  • 只记住了「怎么写」,没记住「会发生什么」;
  • 只在单条规则上做实验,很少在多个 location 组合下做验证。

如果你把这一篇的核心知识点压缩成一张纸,大概就是:

  • http / server / location 的分层职责;
  • 7 种 location 写法的优先级 + 是否继续匹配
  • 5 个例子里「谁先说话、谁最后拍板」的行为模式;
  • rewrite 只改路径,proxy_pass 才是真正转发。

nginx 最容易犯错的地方,不在于你不会写指令,

而在于你没能在脑子里把「真正的匹配过程」想一遍。

下一篇(中篇),我们会把视角从「路由规则」拉到「流量入口」:

从 HTTPS、反向代理、跨域、永久跳转,到泛二级域名这些实际工程场景,

把 nginx 当成一条真正的「流量总入口」来看,而不仅仅是一个静态资源服务器。

相关推荐
JaHeng07231 小时前
nginx的https搭建
运维·nginx·https
啥都学点的程序员1 小时前
python项目调用shardingsphere时,多进程情况下,shardingsphere配置的连接数会乘以进程数
后端
guchen661 小时前
C# 闭包捕获变量的经典问题分析
后端
Lear1 小时前
Lombok全面解析:极致简化Java开发的神兵利器
后端
小周在成长1 小时前
Java 单例设计模式(Singleton Pattern)指南
后端
啥都学点的程序员1 小时前
小坑记录:python中 glob.glob()返回的文件顺序不同
后端
Airene1 小时前
spring-boot 4 相比 3.5.x 的包依赖变化
spring boot·后端
用户3544254365401 小时前
别再裸奔了!你的 Spring Boot @Async 正在榨干服务器资源
后端
虎子_layor1 小时前
小程序登录到底是怎么工作的?一次请求背后的三方信任链
前端·后端