Nginx路由匹配

  |  

Nginx路由匹配

前言

​ 众所周知在nginx.conf配置文件中,可以配置多个虚拟主机,那么在虚拟主机中需要配置一个location。 那这个location是有何作用呢? location有几种匹配模式?,几种匹配模式之间的优先级又是怎么样的?

location的作用

​ location有“定位”的意思,根据请求不同的URL来进行不同的处理。在虚拟主机中(server),location配置是必不可少的,可以把网站不同的部分定位到不同的处理方式上。

location规则

location区段,通过指定模式来与客户端请求的URI相匹配

​ 允许根据用户请求的URI来匹配定义的各location,匹配到时,此请求将被响应的location配置快中的配置所处理,例如做访问控制等功能

语法

1
location [修饰符] pattern {…}
常用的修饰符说明
修饰符功能
前缀匹配 能够匹配以需要匹配的路径为前缀的uri
=精确匹配
~正则表达式模式匹配,区分大小写
~*正则表达式模式匹配,不区分大小写
^~非正则匹配,类似于无修饰符的行为,也是以指定模块开始,不同的是,如果模式匹配,那么就停止搜索其他模式了,不支持正则表达式
/通用匹配,任何请求都会匹配到。
@定义命名location区段,这些区段客户端不能访问,只可以有内部产生的请求访问,如try_files或error_page等

示例

前缀匹配

没有修饰符表示必须以指定模式开始,指定模式前面没有任何修饰符,直接在location后写需要匹配的uri,它的优先级次于正则匹配

1
2
3
4
5
6
server {
server_name www.baibai.com;
location /abc {
......
}
}

那么如下内容可以就可以正确匹配:

通用匹配

通用匹配使用一个 / 表示,可以匹配所有请求,一般nginx配置文件最后都会有一个通用匹配规则,当其他匹配规则均失效时,请求会被路由给通用匹配规则处理;如果没有配置通用匹配,并且其他所有匹配规则均失效时,nginx会返回 404 错误

1
2
3
4
5
6
server {
server_name www.baibai.com;
location /{
......
}
}

那么如下内容可以就可以正确匹配所有请求

精确匹配

精确匹配使用 = 表示,nginx进行路由匹配的时候,精确匹配具有最高的优先级,请求一旦精确匹配成功nginx会停止搜索其他到匹配项

1
2
3
4
5
6
server {
server_name www.baibai.com;
location = /abc {
......
}
}

那么如下内容可正确匹配:

如下内容则无法匹配:

非正则匹配

非正则匹配的优先级仅次于精确匹配,nginx对一个请求精确前缀匹配成功后,停止继续搜索其他到匹配项

1
2
3
4
5
6
server {
server_name www.baibai.com;
location ^~ /abc {
......
}
}

那么如下内容可以就可以正确匹配:

正则表达式

正则匹配分为区分大小写和不区分大小写两种,分别用 ~~* 表示;一个请求精确匹配和精确前缀匹配都失败后,如果配置有相关的正则匹配location,nginx会尝试对该请求进行正则匹配。需要说明的是正则匹配之间没有优先级一说,而是按照在配置文件中出现的顺序进行匹配,一旦匹配上一个,就会停止向下继续搜索

区分大小写

~:表示指定的正则表达式要区分大小写,如:

1
2
3
4
5
6
server {
server_name www.baibai.com;
location ~ ^/abc$ {
......
}
}

那么如下内容可以正确匹配:

如下内容则无法匹配:

不区分大小写

~*:表示指定的正则表达式不区分大小写,如:

1
2
3
4
5
6
server {
server_name www.baibai.com;
location ~* ^/abc$ {
......
}
}

那么如下内容就可以正确匹配:

如下内容则无法匹配:

完整例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
location = / {
echo "规则A";
}
location = /login {
echo "规则B";
}
location ^~ /static/ {
echo "规则C";
}
location ^~ /static/files {
echo "规则X";
}
location ~ \.(gif|jpg|png|js|css)$ {
echo "规则D";
}
location ~* \.png$ {
echo "规则E";
}
location /img {
echo "规则Y";
}
location / {
echo "规则F";
}
请求uri匹配路由规则
http://localhost/规则A
http://localhost/login规则B
http://localhost/register规则F
http://localhost/static/a.html规则C
http://localhost/static/files/a.txt规则X
http://localhost/a.png规则D
http://localhost/a.PNG规则E
http://localhost/img/a.gif规则D
http://localhost/img/a.tiff规则Y

匹配顺序

匹配顺序和优先级,由高到底依次为

  • 带有“=”的精确匹配优先
  • 正则表达式
  • 没有修饰符的精确匹配

具体匹配规则如下:

  • =精准匹配命中时,停止location动作,直接走精准匹配,

  • 一般匹配(含非正则)命中时,先收集所有的普通匹配,最后对比出最长的那一条

  • 如果最长的那一条普通匹配声明为非正则,直接此条匹配,停止location

  • 如果最长的那一条普通匹配不是非正则,继续往下走正则location

  • 按代码顺序执行正则匹配,当第一条正则location命中时,停止location

注:有多个正则表达式出现时,按照它们在配置文件中定义的顺序

path匹配过程

假设http请求路径为 http://192.168.0.132:8088/mvc/index?id=2 ,匹配过程如下:

  • 将整个url拆解为域名/端口/path/params

  • 先由域名/端口,对应到目标server虚拟主机

  • path部分参与location匹配,path = path1匹配部分 + path2剩余部分

  • 进入location方法体内部流程。

  • 若是静态文件处理,则进入目标目录查找文件:root指令时找path1+path2对应的文件;alias指令时找path2对应的文件

  • 若是proxy代理,则形如proxy_pass=ip:port时转发path1+path2路径到tomcat;形如proxy_pass=ip:port/xxx时转发path2路径到tomcat。params始终跟随转发。

实际使用建议

所以实际使用中,个人觉得至少有三个匹配规则定义,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
#这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
proxy_pass http://tomcat:8080/index
}
# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
root /webroot/res/;
}
# 第三个规则就是通用规则,用来转发动态请求到后端应用服务器
# 非静态文件请求就默认是动态请求,自己根据实际把握
# 毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
proxy_pass http://tomcat:8080/
}

location认识的误区

误区1

location 的匹配顺序是“先匹配正则,再匹配普通”

​ 矫正: location 的匹配顺序其实是“先匹配普通,再匹配正则”。我这么说,大家一定会反驳我,因为按“先匹配普通,再匹配正则”解释不了大家平时习惯的按“先匹配正则,再匹配普通”的实践经验。这里我只能暂时解释下,造成这种误解的原因是:正则匹配会覆盖普通匹配(实际的规则,比这复杂,后面会详细解释)。

误区2

location 的执行逻辑跟 location 的编辑顺序无关

​ 矫正:这句话不全对,“普通 location ”的匹配规则是“最大前缀”,因此“普通 location ”的确与 location 编辑顺序无关;但是“正则 location ”的匹配规则是“顺序匹配,且只要匹配到第一个就停止后面的匹配”;“普通location ”与“正则 location ”之间的匹配顺序是?先匹配普通 location ,再“考虑”匹配正则 location 。注意这里的“考虑”是“可能”的意思,也就是说匹配完“普通 location ”后,有的时候需要继续匹配“正则 location ”,有的时候则不需要继续匹配“正则 location ”。两种情况下,不需要继续匹配正则 location :( 1 )当普通 location 前面指定了“ ^~ ”,特别告诉 Nginx 本条普通 location 一旦匹配上,则不需要继续正则匹配;( 2 )当普通location 恰好严格匹配上,不是最大前缀匹配,则不再继续匹配正则。

使用案例

代理静态文件

为什要使用Nginx代理静态文件?

​ 作为后端服务器中间件,Tomcat是绝大多数Java程序员的选择。但是Tomcat处理请求的能力固然很强大,但是作为静态资源代理的话,我们每次这么大的IO吞吐,我只能说:“廉颇老矣,尚能饭否?”。

​ 我们怎么解决这个问题呢静态资源访问这个问题呢,那就是使用Nginx代理服务器文件夹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 10086;
server_name localhost;

location / {
proxy_set_header X-Real-IP remoteaddr;proxysetheaderX−Forwarded−Forproxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://blog.jackyang.me/;
proxy_redirect off;
}

location /data/ {
alias '/usr/local/www/data';
//这里是重点,就是代理这个文件夹 , 访问 http://localhost:10086/data/下面的资源就是访问/usr/local/www/data文件夹的资源
expires 7d;
}
}
root与alias的区别

在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。

  • root的处理结果是:root路径 + location路径
  • alias的处理结果是:使用alias路径替换location路径
  • alias是一个目录别名的定义,root则是最上层目录的定义。
  • 还有一个重要的区别是alias后面必须要用“/”结束,否则会找不到文件的,而root则可有可无。
1
2
3
4
5
6
7
8
9
10
# 如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件。
location ^~ /t/ {
root /www/root/html/;
}

# 如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/new_t/a.html的文件。
# 注意这里是new_t,因为alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。
location ^~ /t/ {
alias /www/root/html/new_t/;
}
注意
  1. 使用alias时,目录名后面一定要加”/“。
  2. alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。
  3. alias只能位于location块中。(root可以不放在location中)

代理服务器(反向代理)

​ 一般上线的项目,出于安全性的考虑,是不允许外网直接访问的,这时候nginx的反向代理功能就起到了关键作用。通常表现为,在生产服务器上部署项目和代理服务器,客户端不能直接访问生产服务器,需要通过nginx接收客户端传来的请求,然后转发给生产服务器,再将服务器的回应发送给客户端。这个闭合过程nginx充当一个中转站,在此过程中,用户不需要配置任何代理ip和端口,或者说客户端根本就不知道自己访问的是真实的服务器还是代理服务器,这样能有效的保证内网的安全 。

后台服务原始路径:http://192.168.0.132:8088/mvc/index?id=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
server {
listen 80;
# 代理的域名,通过该域名可以访问到被代理的服务
server_name baiyp.com;
charset utf-8;
#代理地址无/,访问路径:http://baiyp.com/nginx/mvc/index?id=2
location /mvc {
#此处未关闭,传递整个路径/mvc/index到目标ip:port
proxy_pass http://192.168.0.132:8088;
}
#代理地址有/,访问路径 :http://baiyp.com/nginx/mvc/index?id=2
location /nginx/mvc {
#匹配路径/dynamic,剩余路径/index
proxy_pass http://192.168.0.132:8088/mvc;
}
location / {
# 被代理的服务器地址
proxy_pass http://127.0.0.1:21010;
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}
}

访问控制

注:用于location段,可以用主机地址表示,也可用网段表示,必须一起用
allow:设定允许那台或那些主机访问,多个参数间用空格隔开
deny:设定禁止那台或那些主机访问,多个参数间用空格隔开

1
2
3
4
5
6
location / {
root html;
index index.php index.html index.htm;
allow 192.168.91.129/32;
deny all;
}

跨域访问

使用add_header设置跨域访问

1
2
3
4
5
6
location / {
add_header Access-Control-Allow-Origin www.baidu.com;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
root /usr/share/nginx/html;
index index.html index.htm;
}

防盗链

使用referer防盗链

1
2
3
4
5
6
7
location ~ .*\.(jpg|gif|png)$ {
valid_referers none blocked 119.2x.1x3.218 支持增则匹配;
if ($invalid_referer) {
return 403;
}
root /nginxtest/images;
}

 评论