运维 Nginx location 知识知多少

jqw889 · 2020年07月01日 · 1935 次阅读

写在之前

众所周知 nginx location 路由转发规则多种多样,尤其是 [ = | ~ | ~* | ^~ ] 这些前缀是什么意思、root 与 alias 是否可以区分开,nginx 作为反向代理服务器时,location 中 proxy_pass 中配置的上游服务,服务器后面是否带/与不带/是否有区别,带 URI 与不带 URI 是否有区别等,本文从实践的角度进行分析总结。

location 中 URI 前缀实践

匹配语法

nginx 的 uri 匹配模式很强大、并且使用也非常灵活,下面就是 nginx 的 location 相关的所有语法;

location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }

是的,语法就这一点,是不是感觉很简单呢?

location [指令模式] uri {
...
}

指令模式说明

指令模式有 [ = | ~ | ~* | ^~ ],根据不同的指令模式可分为:

精确匹配:= uri { ... }

前缀匹配:^~ uri { ... }

正则匹配:~ uri { ... } 和 ~* uri { ... }

正常匹配:uri { ... }

全匹配:/ { ... }

指令模式匹配顺序

精确匹配 > 前缀匹配 > 正则匹配 > 正常匹配 > 全匹配

实验说明

测试时,最好不要使用浏览器,因为浏览器有缓存,如果要使用请先清除缓存,或者使用 curl 或者 Postman 工具等。

精确匹配

# 第1段
location /k8svip/ {
root /data/nginx/html4/;
index index.html;
}

# 第2段
location = /k8svip/ {
root /data/nginx/html3/;
index index.html;
}

浏览器访问:http://127.0.0.1/k8svip/

根据上面的匹配顺序,这里应该显示什么呢?大家都会认为是第 2 段代码中的 = /jdb/ 你可以实验下,不是的,是第 1 段,为什么?

精确匹配中 '/k8svip/'中优先匹配到第 2 段,再访问'/k8svip/index.html',此次内部跳转 uri 已经是'/k8svip/index.html',而非 =的;最终访问结果是第 1 段中的 index.html。

# 第3段
location /k8svip/ {
rewrite ^/k8svip/$ https://www.baidu.com/ break;
}

# 第4段
location = /k8svip/ {
rewrite ^/k8svip/$ https://www.sina.com.cn/ break;
}

浏览器访问:http://127.0.0.1/k8svip/ 看下效果,是不是匹配到第 4 段了呢?答案:是的;访问http://127.0.0.1/k8sVIP/http://127.0.0.1/k8svip/abc 再看下效果,答案:都无法正常访问。这里可结合之前的一篇:看了这篇!再不会 Nginx rewrite 算我输!

结论:精确匹配区分大小写,不能使用正则,访问的 URI 必须完全与=后面的一致,多一个"/"或者少一个"/",都是不可以的

前缀匹配

location ^~ /k8svip/ {
rewrite ^ https://www.163.com break;
}

location ^~ /k8svip/bcd/ {
rewrite ^ https://www.qq.com break;
}

location ^~ /Abc/ {
rewrite ^ https://www.sina.com.cn break;
}

访问:http://127.0.0.1/k8svip/ 成功,跳转到 163 首页;

访问:http://127.0.0.1/k8svip/abcd/163首页;成功,只匹配前缀,跳转到

访问:http://127.0.0.1/K8svip/ 不成功,区分大小写,出错;

访问:http://127.0.0.1/k8sVip/123 不成功,区分大小写,出错;

访问:http://127.0.0.1/Abc/ 成功,区分大小写,跳转到新浪首页;

访问:http://127.0.0.1/Abc/abc 成功,只匹配前缀,跳转到新浪首页;

结论:前缀匹配不能使用正则,区分大小写,只要前缀相同,都可以匹配成功,不管后面有没有字符,保证前缀相同即可。

正则匹配

location ~ /[a-z]k8svip/ {
rewrite ^ https://www.sina.com.cn break;
}

location ~* /[a-z]k8svip/ {
rewrite ^ https://www.google.com break;
}

访问:http://127.0.0.1/ak8svip/ 成功,正则匹配,会跳转到新浪首页;

访问:http://127.0.0.1/ak8svip/bbb 成功,正则 + 前缀匹配,会跳转到新浪首页;

访问:http://127.0.0.1/zk8svip/ 成功,会跳转到新浪首页;

访问:http://127.0.0.1/Ak8svip/aaa 成功,这个会跳转到 google.com 首页;

结论:正则匹配 ~ 区分大小写,~* 不区分大小写,并且与前缀匹配比较类似,只需要匹配模式开头部分,这两种同时存在时,优先匹配区分大小写的。

正常匹配(指令模式为空的匹配规则叫正常匹配)

# 第1段
location /k8svip/ {
rewrite ^ https://www.google.com break;
}

# 第2段
location /[0-9]k8svip/ {
rewrite ^ https://www.qq.com break;
}

访问:http://127.0.0.1/k8svip/ 成功,跳转 google 首页;

访问:http://127.0.0.1/k8svip/2 成功,类似前缀匹配,跳转 google 首页;

访问:http://127.0.0.1/k8sVIP/ 不成功,区分大小写,出错;

访问:http://127.0.0.1/0k8svip/ 不成功,不支持正则;

结论:有些文档中说正常匹配支持正则,不区分大小写,个人测试了下,不支持正则,但在 uri 后面继续跟字符,区分大小写(相信实验结果😀,大家可以测试,如果有问题,可以交流,也有可能是版本的差异)正常匹配与前缀匹配的差别,只在于优先级。

# 第3段
location ^~ /k8svip/ {
rewrite ^/k8svip/$ https://www.baidu.com/ break;
}

# 第4段
location /k8svip/ {
rewrite ^/k8svip/$ https://www.sina.com.cn/ break;
}

总结:第 3 段前缀匹配与第 4 段正常匹配不能同时存在,否则会报 nginx: [emerg] duplicate location "/k8svip/" in /data/nginx//conf/nginx.conf:47 因为这两个都是普通字符串;

全匹配

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

说明:没有匹配指令,并且匹配的 URI 仅一个斜杠 /,通常用在一个默认页面的地方,这个地方没有什么好说的。

命名匹配

error_page 404 = @notfound;
location @notfound {
rewrite ^ https://www.google.com break;
}

说明:一般用于静态页面或者错误页面,并且这个命名匹配中,不允许有 alias。

优先级验证综合实验

# 全匹配,这里/data/nginx/html/下面有一个k8svip文件夹,里面有index.html
location / {
root /data/nginx/html;
index index.html;
}

# 正常匹配
location /k8svip/ {
rewrite ^/k8svip/$ https://www.sina.com.cn/ break;
}

# 正则匹配
location ~ /[a-z]8svip/ {
rewrite ^/k8svip/$ https://www.google.com/ break;
}

# 前缀匹配
location ^~ /k8svip/ {
rewrite ^/k8svip/$ https://www.baidu.com/ break;
}

# 精确匹配
location = /k8svip/ {
rewrite ^/k8svip/$ https://www.qq.com/ break;
}

测试方法:把最先匹配的注释掉,然后再继续测试,就证明了上面的优先级,注意前缀匹配与正常匹配在相同 URI 时,不能同时存在;

说明:为了证明以上的优先级顺序,我们设置了以上的测试用例,为了更进一步说明问题,我们把优先级最高的放在配置文件的最下面,这样避免从上到下优先匹配的问题,通过上面的实验,可以验证 精确匹配 > 前缀匹配 > 正则匹配 > 正常匹配 > 全匹配 这个指令优先级顺序。

匹配原则除了这个优先级外,还有一个就是在相同指令模式匹配中,匹配度最大的 URI 优先; root 与 alias

nginx 配置 location 规则中的 uri 往往都是匹配一个目录。

location /k8svip/ {
root /data/nginx/html/;
index index.html index.htm;
}

当我们访问 http://127.0.0.1/k8svip/index.html/data/nginx/html/时,如果 这个目录下面没有 k8svip 文件夹,并且它下面没有 index.html 或者 index.htm,会报 404 错误?原因是使用 root 指定目录时,目录下面需要包括 location 后面的 URI,否则会报错,即/data/nginx/html/k8svip/index.html,也可以这样理解,root 指定目录时,不会将 location uri 配置的路径去掉,需要在 root 目录下面包括 uri 路径;

location /k8svip/ {
alias /data/nginx/html/;
index index.html index.htm;
}

当我们访问 http://127.0.0.1/k8svip/index.html时,这里使用 alias,只要保证 alias 指定的目录中有 index.html 即可,它会将 location uri 配置的路径去掉,实际访问的就是/data/nginx/html/index.html。

结论:root 指定的目录中,需要 location 中的 uri 路径目录确实存在,alias 指定的目录中不需要 location 中的 uri 路径目录存在。

代理服务器 proxy_pass 中有无 /

nginx 作为反向代理服务器时,会使用到 proxy_pass,但是 proxy_pass 后面 server 有无 / 是有很大区别的,今天通过实验做下总结;

上游真实 server 192.168.1.3 配置

location /k8svip/ {
root /data/nginx/html/;
index index.html index.htm;
}

location /k8svi {
root /data/nginx/html/;
index index.html index.htm;
}

location /k3svip/ {
root /data/nginx/html/;
index index.html index.htm;
}

上游真实 server 192.168.1.3 验证

[root@vm-os-centos7 conf]# curl http://192.168.1.3/k8svip/
k8svip
[root@vm-os-centos7 conf]# curl -L http://192.168.1.3/k8svi
k8svi
[root@vm-os-centos7 conf]# curl http://192.168.1.3/k3svip/
k3svip
[root@vm-os-centos7 html]# curl http://192.168.1.3/k3svipindex
k3svipindex.html
[root@vm-os-centos7 html]#

代理服务器 proxy_pass 中无 "/" 验证

# 实验 反向代理中后面没有"/"
location /k8svip/ {
proxy_pass http://192.168.1.3;
}

# 实验结果
[root@vm-os-centos7 ~]# curl http://192.168.1.2/k8svip/
k8svip
[root@vm-os-centos7 ~]# curl -L http://192.168.1.2/k8svip
k8svip
[root@vm-os-centos7 ~]#

192.168.1.3 access.log 日志如下:

192.168.1.2 - - [17/Jun/2020:21:04:58 +0800] "GET /k8svip/ HTTP/1.0" 200 7 "-" "curl/7.29.0"

结论:如果 proxy_pass 反向代理中,在 server 后面没有 "/" 反斜杠的话,访问的是 http://192.168.1.3/k8svip/index.htmllocation,它会去上游真实服务器去匹配代理服务器上面 URI。

代理服务器 proxy_pass 中有 "/" 验证

# 实验 proxy_pass 后面server中有"/"
location /k8svip/ {
  proxy_pass http://192.168.1.3/;
}
# 实验结果
[root@vm-os-centos7 conf]# curl http://192.168.1.2/k8svip/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
</body>
</html>
[root@vm-os-centos7 conf]#
192.168.1.3 access.log 日志如下: 192.168.1.2 - - [17/Jun/2020:21:11:17 +0800] "GET / HTTP/1.0" 200 257 "-" "curl/7.29.0" 结论:如果 proxy_pass 反向代理中,在 server 后面有 "/" 反斜杠的话,访问的是 http://192.168.1.3/index.html,它会忽略掉代理服务器上面location中的URI,直接访问代理服务器上面的"/"。 代理服务器 proxy_pass 后面增加目录并且加"/"''
# 实验 proxy_pass 后面有目录,并且目前后面有"/"
location /k8svip/ {
proxy_pass http://192.168.1.3/k3svip/;
}

# 实验结果
[root@vm-os-centos7 conf]# curl http://192.168.1.2/k8svip/
k3svip
[root@vm-os-centos7 conf]#

192.168.1.3 access.log 日志如下:

192.168.1.2 - - [17/Jun/2020:21:19:11 +0800] "GET /k3svip/ HTTP/1.0" 200 7 "-" "curl/7.29.0"

结论:如果 proxy_pass 反向代理中有目录,并且有"/"反斜杠的话,访问的是http://192.168.1.3/k3svip/index.htmllocation中的URI,直接访问代理服务器上面的目录+"/"的形式。,它会忽略掉代理服务器上面

代理服务器 proxy_pass 后面增加 URI,但不加"/"

# proxy_pass 中有目录,没有"/"
location /k8svip/ {
proxy_pass http://192.168.1.3/k3svipindex;
}

# 实验结果
[root@vm-os-centos7 conf]# curl http://192.168.1.2/k8svip/
k3svipindex.html
[root@vm-os-centos7 conf]#

192.168.1.3 access.log 日志如下:

192.168.1.2 - - [17/Jun/2020:21:36:19 +0800] "GET /k3svipindex HTTP/1.0" 200 17 "-" "curl/7.29.0"

结论:如果 proxy_pass 反向代理中,在 server 后面有 URI,但没有 "/" 反斜杠的话,访问的是 http://192.168.1.3/k3svipindexURI.,它会去上游真实服务器去匹配代理服务器上面的

总结

本文通过实验总结了 location 的匹配规则、root 与 alias 的区别、以及 nginx 作为反向代理服务器时,proxy_pass 中真实 server 是否有"/"反斜杠的区别等。

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号