一文理清nginx中的location配置
location 指令是 nginx 中最关键的指令之一,location 指令的功能是用来匹配不同的 URI 请求,进而对请求做不同的处理和响应,这其中较难理解的是多个 location 的匹配顺序,本文会作为重点来解释和说明。
开始之前先明确一些约定,我们输入的网址叫做请求 URI,nginx 用请求 URI 与 location 中配置的 URI 做匹配。
nginx文件结构
首先我们先简单了解 nginx 的文件结构,nginx 的 HTTP 配置主要包括三个区块,结构如下:
1 | Global: nginx 运行相关 |
从上面展示的 nginx 结构中可以看出 location 属于请求级别配置,这也是我们最常用的配置。
配置 location 块
location 语法
Location 块通过指定模式来与客户端请求的URI相匹配。
Location基本语法:
- 匹配 URI 类型,有四种参数可选,当然也可以不带参数。
- 命名location,用@来标识,类似于定义goto语句块。
1 | location [ = | ~ | ~* | ^~ ] /URI { … } |
location匹配命令解释
参数 | 解释 |
---|---|
空 |
location 后没有参数直接跟着 标准 URI,表示前缀匹配,代表跟请求中的 URI 从头开始匹配。 |
= |
用于标准 URI 前,要求请求字符串与其精准匹配,成功则立即处理,nginx停止搜索其他匹配。 |
^~ |
用于标准 URI 前,并要求一旦匹配到就会立即处理,不再去匹配其他的那些个正则 URI,一般用来匹配目录 |
~ |
用于正则 URI 前,表示 URI 包含正则表达式, 区分大小写 |
~\* |
用于正则 URI 前, 表示 URI 包含正则表达式, 不区分大小写 |
@ |
@ 定义一个命名的 location,@ 定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。 |
location匹配顺序
nginx有两层指令来匹配请求 URI 。第一个层次是 server 指令,它通过域名、ip 和端口来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。
location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求URI 会按如下规则进行匹配:
- 先精准匹配
=
,精准匹配成功则会立即停止其他类型匹配; - 没有精准匹配成功时,进行前缀匹配。先查找带有
^~
的前缀匹配,带有^~
的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数^~
)成功则会暂存,继续查找正则匹配; =
和^~
均未匹配成功前提下,查找正则匹配~
和~\*
。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;- 所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数
^~
)结果
以上规则简单总结就是优先级从高到低依次为(序号越小优先级越高):
1 | 1. location = # 精准匹配 |
上述匹配规则可以用以下伪代码表示,加深理解:
1 | function match(uri): |
案例分析
接下来,让我们通过一些实际案例来验证上述规则。
案例 1
1 | server { |
说明:按照上述的规则,显然第二个正则匹配会有更高的优先级
案例 2
1 | server { |
说明:第二个匹配了正则表达式,优先级高于第一个普通前缀匹配
案例 3
1 | server { |
说明:第一个前缀匹配 ^~
命中以后不会再搜寻正则匹配,所以会第一个命中。
案例 4
1 | server { |
说明:前缀匹配下,返回最长匹配的 location,与 location 所在位置顺序无关
案例 5
1 | server { |
把顺序换一下
1 | server { |
说明:可见正则匹配是使用文件中的顺序,先匹配成功的返回。
案例 6
我们对一个官方文档中提到例子做一些补充,来看一个相对较完整的例子,假设我们有如下几个请求等待匹配:
1 | / |
以下是 location 配置及其匹配情况
1 | location = / { |
其他location配置相关
匹配问号后的参数
请求 URI 中问号后面的参数是不能在 location 中匹配到的,这些参数存储在 $query_string
变量中,可以用 if
来判断。
例如,对于参数中带有单引号 ’ 进行匹配然后重定向到错误页面。
1 | /plus/list.php?tid=19&mid=1124‘ |
location URI结尾带不带 /
关于 URI 尾部的 /
有三点也需要说明一下。第一点与 location 配置有关,其他两点无关。
- location 中的字符有没有
/
都没有影响。也就是说/user/
和/user
是一样的。 - 如果 URI 结构是
https://domain.com/
的形式,尾部有没有/
都不会造成重定向。因为浏览器在发起请求的时候,默认加上了/
。虽然很多浏览器在地址栏里也不会显示/
。这一点,可以访问baidu验证一下。 - 如果 URI 的结构是
https://domain.com/some-dir/
。尾部如果缺少/
将导致重定向。因为根据约定,URL 尾部的/
表示目录,没有/
表示文件。所以访问/some-dir/
时,服务器会自动去该目录下找对应的默认文件。如果访问/some-dir
的话,服务器会先去找some-dir
文件,找不到的话会将some-dir
当成目录,重定向到/some-dir/
,去该目录下找默认文件。可以去测试一下你的网站是不是这样的。
命名 location
带有 @
的 location 是用来定义一个命名的 location,这种 location 不参与请求匹配,一般用在内部定向。用法如下:
1 | location / { |
上例中,当尝试访问 URI 找不到对应的文件就重定向到我们自定义的命名 location(此处为 custom)。
值得注意的是,命名 location 中不能再嵌套其它的命名 location。
location 实际使用建议
所以实际使用中,个人觉得至少有三个匹配规则定义,如下:
直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
这里是直接转发给后端应用服务器了,也可以是一个静态首页。第一个必选规则:
1 | location = / { |
第二个必选规则是处理静态文件请求,这是 nginx 作为 http 服务器的强项,有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用:
1 | location ^~ /static/ { |
第三个规则就是通用规则,用来转发动态请求到后端应用服务器,非静态文件请求就默认是动态请求,自己根据实际把握,毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了:
1 | location / { |
nginx 通过 ngx_http_rewrite_module
模块支持 URI 重写、支持 if 条件判断,但不支持 else。
rewrite 只能放在 server { } 、 location { } 、 if { } 中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如http://aaa.com/a/we/index.php?id=1&u=str
只对/a/we/index.php重写。语法为 rewrite regex replacement [flag];
指令执行顺序
表面看 rewrite 和 location 功能有点像,都能实现跳转,主要区别在于 rewrite 是在同一域名内更改获取资源的路径,而 location 是对一类路径做控制访问或反向代理,可以 proxy_pass 到其他机器。很多情况下 rewrite 也会写在 location 里,它们的执行顺序是:
- 执行 server 块的 rewrite 指令(这里的块指的是 server 关键字后{}包围的区域,其它 xx 块类似)
- 执行location匹配
- 执行选定的location中的rewrite指令
如果其中某步 URI 被重写,则重新循环执行1-3,直到找到真实存在的文件;
如果循环超过 10
次,则返回 500 Internal Server Error
错误。
指令详解
if 指令
语法:if(condition) {…}
作用域: server
、location
功能:对给定的条件 condition 进行判断。如果为真,大括号内的 rewrite 指令将被执行。if 条件 (conditon) 可以是如下任何内容:
if 中的几种判断条件
- 一个变量名;false 如果这个变量是空字符串或者以0开始的字符串;
- 使用
=
,!=
比较的一个变量和字符串 - 是用
~
,~*
与正则表达式匹配的变量,如果这个正则表达式中包含},;则整个表达式需要用” 或’ 包围 - 使用
-f
,!-f
检查一个文件是否存在 - 使用
-d
,!-d
检查一个目录是否存在 - 使用
-e
,!-e
检查一个文件、目录、符号链接是否存在 - 使用
-x
,!-x
检查一个文件是否可执行
示例:
1 | set $variable "0"; |
return 指令
语法:return code [text];
return code URL;
return URL;
作用域:server
,location
,if
功能:停止处理并将指定的 code 码返回给客户端。 非标准 code 码 444 关闭连接而不发送响应报头。
该指令用于检查一个条件是否符合,如果条件符合,则执行大括号内的语句。If 指令不支持嵌套,不支持多个条件 && 和 || 处理。
从0.8.42版本开始, return 语句可以指定重定向 URI (状态码可以为如下几种 301,302,303,307),
也可以为其他状态码指定响应的文本内容,并且重定向的 URI 和响应的文本可以包含变量。
有一种特殊情况,就是重定向的url可以指定为此服务器本地的 URI,这样的话,nginx 会依据请求的协议 $scheme
, server_name_in_redirect
和 port_in_redirect
自动生成完整的 URI。
示例:如果访问的 URI 以 .sh
或 .bash
结尾,则返回 403
状态码
1 | location ~ .*\.(sh|bash)?$ |
rewrite 指令
语法:rewrite regex replacement [flag];
作用域:server
、location
、if
功能:如果一个URI匹配指定的正则表达式regex,URI就按照 replacement 重写。
rewrite 按配置文件中出现的顺序执行。可以使用 flag 标志来终止指令的进一步处理。
如果 replacement 以 http://
、 https://
或 $ scheme
开始,将不再继续处理,这个重定向将返回给客户端。
示例:第一种情况,重写的字符串带 http://
1 | location ^~ /redirect { |
在浏览器中输入 127.0.0.1:8080/redirect/baidu
,则临时重定向到 www.baidu.com
后面的 return 指令将没有机会执行了。
1 | location ^~ /redirect { |
此处没有带 http://
所以只是简单的重写。请求的 URI 由 /test1/baidu 重写为 www.baidu.com
因为会顺序执行 rewrite 指令,所以 下一步执行 return 指令,响应后返回 ok
flag
有四种参数可以选择:
last
停止处理后续 rewrite 指令集,然后对当前重写的新 URI 在 rewrite 指令集上重新查找。break
停止处理后续 rewrite 指令集,并不再重新查找,但是当前location 内剩余非 rewrite 语句和 location 外的 非rewrite 语句可以执行。redirect
如果replacement
不是以http://
或https://
开始,返回302
临时重定向permanent
返回301
永久重定向
示例 1:
1 | # rewrite 后面没有任何 flag 时就顺序执行 |
如果正则表达regex式中包含 “}
” 或 “;
”,那么整个表达式需要用双引号或单引号包围。
示例 2:
1 | location / { |
发送如下请求 curl 127.0.0.1:8080/test1
返回 /test3
当如果将上面的 location / 改成如下代码
1 | location / { |
发送请求 127.0.0.1:8080/test1
代理到 https://www.baidu.com
rewrite 后的请求参数:
如果替换字符串 replacement
包含新的请求参数,则在它们之后附加先前的请求参数。如果你不想要之前的参数,则在替换字符串 replacement
的末尾放置一个问号,避免附加它们。
1 | # 由于最后加了个 ?,原来的请求参数将不会被追加到 rewrite 之后的 URI 后面* |
rewrite_log 指令
语法:rewrite_log on | off;
默认值:rewrite_log off;
作用域:http
、 server
、 location
、 if
功能:开启或关闭以 notice 级别打印 rewrite 处理日志到 error log 文件。
set 指令
语法:set variable value;
默认值:none
作用域:server
、 location
、 if
定义一个变量并赋值,值可以是文本,变量或者文本变量混合体。
uninitialized_variable_warn 指令
作用域:http
、 server
、 location
、 if
语法:uninitialized_variable_warn on | off;
默认值:uninitialized_variable_warn on
功能:控制是否记录 有关未初始化变量的警告。
一文理清nginx中的location配置