一文彻底读懂nginx中的location指令
location指令是nginx中最关键的指令之一,location指令的功能是用来匹配不同的url请求,进而对请求做不同的处理和响应,这其中较难理解的是多个location的匹配顺序,本文会作为重点来解释和说明。
开始之前先明确一些约定,我们输入的网址叫做请求URI,nginx用请求URI与location中配置的URI做匹配。
location格式
location有两种格式:
- 匹配uri类型,有四种参数可选,当然也可以不带参数。
- 命名location,用@来标识,类似于定义goto语句块。
location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }
location匹配参数解释
参数 | 解释 |
---|---|
空 | location后没有参数直接跟着URI,表示前缀匹配,代表跟请求中的URI从头开始匹配。 |
~ | 执行一个正则匹配,区分大小写。 |
~* | 执行一个正则匹配,不区分大小写。 |
^~ | 普通字符匹配,多用来匹配目录。 |
= | 执行普通字符精确匹配。 |
@ | "@" 定义一个命名的 location,@定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。 |
location匹配顺序
nginx有两层指令来匹配请求URI。第一个层次是server指令,它通过域名、ip和端口来做第一层级匹配,当找到匹配的server后就进入此server的location匹配。location的匹配并不完全按照它们在配置文件中出现的顺序来匹配,请求URI会按如下规则跟server里配置的location匹配。
- 寻找有没有“=”等号参数完全匹配的location,如果有完全匹配的等号location则停止匹配,执行该location中的指令,不去匹配其它类型的location。
- 匹配所有非正则表达式URI的location(包括空,=,^~三种参数)。找到请求URI和location URI按前缀匹配最长的location,如果这个最长的location的参数是^~,则停止匹配,执行该location中的指令,否则暂存该location。
- 匹配正则表达式URI的location(包括~,~*两种参数),按location在配置文件中出现的顺序匹配,如果找到第一个匹配的locaiton则停止匹配,执行该location。
- 匹配完所有正则表达式都没有匹配的location,则执行第二步中暂存的最长前缀匹配location。
简单来说按这个规则:=
> ^~
> ~
= ~*
>最长前缀匹配
> /
匹配问号后的参数
请求URI中问号后面的参数是不能在location中匹配到的,这些参数存储在$query_string变量中,可以用if来判断。
例如,对于参数中带有单引号'进行匹配然后重定向到错误页面。
/plus/list.php?tid=19&mid=1124'
1 2 3 |
if ( $query_string ~* ".*[;'<>].*" ){ return 404; } |
location URI结尾带不带/
这个很多解释不太准确,我有必要多说几句。
对于请求URI结尾是否带有/,一般的处理逻辑是带/表示访问目录,不带/表示访问文件,如果文件不存在也会去匹配目录。例如访问http://blog.redis.com.cn/images/和http://blog.redis.com.cn/images,前面的请求会匹配目录,后面的请求会先匹配文件,文件不存再匹配目录
对于locatioin中的URI来说,如果URI的结尾带有/,并且location要执行的命令式是proxy_pass、fastcgi_pass、uwsgi_pass、scgi_pass、memcached_pass、grpc_pass之一。例如:
1 2 3 |
location /images/{ proxy_pass http://www.redis.com.cn } |
对于这种情况,nginx会做特殊处理,不管images命名的文件或目录存在不在,如果你访问http://blog.redis.com.cn/images会被重定向到http://blog.redis.com.cn/images/。
所以如果你想这两种请求对应不同的处理,就要明确增加不带/结尾的location配置。
1 2 3 4 5 6 |
location /images { proxy_pass http://www.rabbitmq.cn } location /images/ { proxy_pass http://www.redis.com.cn } |
命名location
带有"@"的location是用来定义一个命名的location,这种location不参与请求匹配,一般用在内部定向。例如用在error_page, try_files命令中。它的功能类似于编程中的goto。
1 2 3 4 5 6 |
location /images { try_files $uri $uri/ @name; } location @name { ... } |
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
location = / { # 只匹配请求 "/" [ configuration A ] } location / { # 匹配任何请求,因为所有请求都是以"/"开始 # 但是更长字符匹配或者正则表达式匹配会优先匹配 [ configuration B ] } location /documents/ { # 匹配所有 /documents/ 开头的请求,在没有正则表达 # 式匹配时选择该locaiton [ configuration C ] } location ^~ /images/ { # 匹配任何以 /images/ 开始的请求,并停止匹配其它location [ configuration D ] }E location ~* .(gif|jpg|jpeg)$ { # 匹配以 gif, jpg, or jpeg结尾的请求. # 但是所有 /images/ 目录的请求将由 [Configuration D]处理. [ configuration E ] } |
请求URI例子:
- / -> 匹配A
- /index.html -> 匹配B
- /documents/a.html -> 匹配C
- /images/1.gif -> 匹配D
- /documents/1.jpg -> 匹配E
不知道有没有解释不清楚的地方,希望没把大家带沟里去。
留言告诉我吧。
笔误 是^~
到底是 ^~ 还是 ~^ ???
2篇文章说的不一样
https://blog.redis.com.cn/115.html
location匹配顺序第二个规则写错,应为:= > ^~ > ~ = ~* >最长前缀匹配 > /
阅读完后收获很大,感谢总结和分享,希望后期在我个人思否博客【云鱼Cloudy】和公众号【前端冰可乐】的技术文章中的引用本文的部分内容,会注明本文地址,非常感谢!