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 | server { |
那么如下内容可以就可以正确匹配:
通用匹配
通用匹配使用一个 / 表示,可以匹配所有请求,一般nginx配置文件最后都会有一个通用匹配规则,当其他匹配规则均失效时,请求会被路由给通用匹配规则处理;如果没有配置通用匹配,并且其他所有匹配规则均失效时,nginx会返回 404 错误
1 | server { |
那么如下内容可以就可以正确匹配所有请求
精确匹配
精确匹配使用 = 表示,nginx进行路由匹配的时候,精确匹配具有最高的优先级,请求一旦精确匹配成功nginx会停止搜索其他到匹配项
1 | server { |
那么如下内容可正确匹配:
如下内容则无法匹配:
非正则匹配
非正则匹配的优先级仅次于精确匹配,nginx对一个请求精确前缀匹配成功后,停止继续搜索其他到匹配项
1 | server { |
那么如下内容可以就可以正确匹配:
正则表达式
正则匹配分为区分大小写和不区分大小写两种,分别用 ~ 和 ~* 表示;一个请求精确匹配和精确前缀匹配都失败后,如果配置有相关的正则匹配location,nginx会尝试对该请求进行正则匹配。需要说明的是正则匹配之间没有优先级一说,而是按照在配置文件中出现的顺序进行匹配,一旦匹配上一个,就会停止向下继续搜索
区分大小写
~:表示指定的正则表达式要区分大小写,如:
1 | server { |
那么如下内容可以正确匹配:
如下内容则无法匹配:
不区分大小写
~*:表示指定的正则表达式不区分大小写,如:
1 | server { |
那么如下内容就可以正确匹配:
如下内容则无法匹配:
完整例子
1 | location = / { |
请求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 | #直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。 |
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 | server { |
root与alias的区别
在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。
- root的处理结果是:root路径 + location路径
- alias的处理结果是:使用alias路径替换location路径
- alias是一个目录别名的定义,root则是最上层目录的定义。
- 还有一个重要的区别是alias后面必须要用“/”结束,否则会找不到文件的,而root则可有可无。
1 | # 如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件。 |
注意
- 使用alias时,目录名后面一定要加”/“。
- alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。
- alias只能位于location块中。(root可以不放在location中)
代理服务器(反向代理)
一般上线的项目,出于安全性的考虑,是不允许外网直接访问的,这时候nginx的反向代理功能就起到了关键作用。通常表现为,在生产服务器上部署项目和代理服务器,客户端不能直接访问生产服务器,需要通过nginx接收客户端传来的请求,然后转发给生产服务器,再将服务器的回应发送给客户端。这个闭合过程nginx充当一个中转站,在此过程中,用户不需要配置任何代理ip和端口,或者说客户端根本就不知道自己访问的是真实的服务器还是代理服务器,这样能有效的保证内网的安全 。
1 | server { |
访问控制
注:用于location段,可以用主机地址表示,也可用网段表示,必须一起用
allow:设定允许那台或那些主机访问,多个参数间用空格隔开
deny:设定禁止那台或那些主机访问,多个参数间用空格隔开
1 | location / { |
跨域访问
使用add_header设置跨域访问
1 | location / { |
防盗链
使用referer防盗链
1 | location ~ .*\.(jpg|gif|png)$ { |