location 的匹配是find_conf这个模块提供的功能,这个模块是十分重要的一个阶段,在nginx的11个处理顺序上, rewrite -> find_config -> rewrite 。 find_conf 是加在两个rewrite 操作之间的,而find_conf的作用呢,一句话概况就是,这个url 到底由哪个location匹配并进行进一步操作。
nginx 的 rewrite 模块的链接 https://www.jianshu.com/p/74bf196b63cc
find_conf 的 最重要的一个指令当然就是location了,除此之外还有一个merge_slashes指令。
find_conf
- location 指令
常规匹配,
精准匹配 = ,
匹配上后不再进行正则表达式匹配 ^~
正则匹配 ~ , ~*(忽略大小写) - merge_lashes 指令
这个指令默认是打开的 merge_lashes on; 它的作用就是将url中的两个 // 合并成一个 /。一般都不需要管,如果url中做了base64编码,这时不能将/合并,这时就需要将// 合并了。
这里主要的还是location的匹配了,这也是最难的问题,有人会误以为这是rewrite模块提供的功能,其实不然,这是find_conf提供的。
下面提供一个location的匹配顺序,其实,也正如下图所示,请区分
^~ 、= 、正则的关系。被 ^~ 匹配上了,就不再做正则匹配了。
在nginx中有一个 binary tree存放所有的 前缀字符串。
location 匹配顺序.png
有如下的nginx配置。
location ~ /Test/$ {
return 200 'first regular expressions match!\n';
}
location ~* /Test1/(\w+)$ {
return 200 'longest regular expressions match!\n';
}
location ^~ /Test1/ {
return 200 'stop regular expressions match!\n';
}
location /Test1/Test2 {
return 200 'longest prefix string match!\n';
}
location /Test1 {
return 200 'prefix string match\n';
}
location = /Test1 {
return 200 'exact match!';
}
如果访问的是如下链接都会怎么样返回呢?
/Test1
/Tesr1/
/Test1/Test2
/Test1/Test2
/test1/Test2
下面一个一个分析
- /Test1
这个路径不用说,由于匹配到了精准匹配,所以返回当然是 exact match!
# curl -H"Host:location.example.wjx" http://127.0.0.1/Test1
exact match!
- /Test1/
这个路径比第一个多了一个'/'。最长匹配,会被 ^~ 匹配到,而且不再做新的正则匹配了。所以返回是 stop regular expressions match
# curl -H"Host:location.example.wjx" http://127.0.0.1/Test1/
stop regular expressions match!
- /Test1/Test2
这个路径可以被 /Test1 , /Test1/Test2,~* /Test1/(\w+) 匹配上, longest prefix string match!
# curl -H"Host:location.example.wjx" http://127.0.0.1/Test1/Test2
longest regular expressions match!
- /Test1/Test2/
这个路径被 和上面的一样,但是经过最长匹配路径的原则,因为有个/,所以被(\w+)$ 匹配不上。是被/Test1/Test2 匹配
# curl -H"Host:location.example.wjx" http://127.0.0.1/Test1/Test2/
longest prefix string match!
- /test1/Test2
不用说,当然是忽律大小写的匹配上啦。
结合反向代理proxy_pass
其实在日常的操作中都会遇到怎么将一个请求反向代理到后端的场景,而事实上,nginx的最大的用处就是做反向代理的用途。
但是 location 加/ 和 proxy加 / ,很多人高不清楚。
其实 proxy加根,表示是以绝对路径访问,就是说,location中的东西一律不带到后面去。
如 我访问 http://proxy.example.wjx/v1/freeins
如下配置
location /v1/ {
proxy_pass http://server1_backend;
}
后端得到的请求是
"GET /v1/freeins HTTP/1.0"
如果后端不想要 /v1 怎么办呢。proxy_pass 后加/ 就可以了。
location /v1/ {
proxy_pass http://server1_backend/;
}
后端得到的请求是
"GET /freeins HTTP/1.0"
但是!!!
location 如果使用的正则表达式的话,在proxy_pass 后面就不能再加 路径了。如下配置
location ~ ^/v1/ {
proxy_pass http://server1_backend/;
}
如果reload的话,直接会报错。
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location,
or inside "if" statement, or inside "limit_except" block in /usr/local/openresty/nginx/conf/../site_enables/proxy_pass.conf:31
这是因为nginx是以模块化的方式构建的,每个配置块都是由各个模块在各个阶段读取的。所以请记住,proxy_pass在以下情况下,指令中不能有URI :
- 正则表达式位置
- 命名的地点
- if 块
实战
那好吧,假设已有如下配置,它被反向代理到server1 的backend。那我想将 /v1/freeins 开头的反代到 server2 中且 不能将v1 带到后端去,这该怎么办呢?
location ~ ^/v1/ {
proxy_pass http://server1_backend;
}
解决
其实使用 ^~ 就可以了。
看看配置
location ~ ^/v1/ {
proxy_pass http://server1_backend;
}
location ^~ /v1/freeins {
proxy_pass http://server2_backend/freeins;
}
# curl -H"Host:proxy.loki.wjx" http://127.0.0.1/v1/freeins/getProduct?id=13232453
后端接收到的请求。
"GET /freeins/getProduct?id=13232453 HTTP/1.0"
网友评论