写在之前
众所周知 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/ 不成功,区分大小写,出错;
访问: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.html,它会去上游真实服务器去匹配代理服务器上面location 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.html,它会忽略掉代理服务器上面location中的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/k3svipindex,它会去上游真实服务器去匹配代理服务器上面的URI。
总结
本文通过实验总结了 location的匹配规则、root与alias的区别、以及nginx作为反向代理服务器时,proxy_pass 中真实server 是否有"/"反斜杠的区别等。
共同学习,写下你的评论
评论加载中...
作者其他优质文章