Nginx server匹配规则

文章转载自:https://www.nginx.cn/5014.html

Nginx的块配置

Nginx在逻辑上将提供不同内容的配置划分为块,这些块以层次结构的形式存在(http->server->location)。客户端发出请求时,Nginx收到之后,会有一个确定应该使用哪些配置块来处理请求的过程。本文主要介绍 server 块背后的处理过程。

server块是Nginx配置的子集,它定义用于处理已定义类型请求的虚拟服务器(虚拟机)。管理员通常会配置多个server块,并根据请求的域名,端口和IP地址决定哪个块应该处理哪个连接。

Nginx如何决定哪个server块来处理请求

由于Nginx允许管理员定义多个server块作为单独的虚拟Web服务器实例,因此需要一个算法来确定将使用哪些server块来匹配请求。

Nginx在此过程中关注的主要server块指令是listen指令和server_name指令。

解析“listen”指令以找到可能的匹配

首先,Nginx查看请求的IP地址和端口,并与每个服务器的 listen 指令相匹配,构建可能解析请求的服务器块列表。

listen指令通常定义 server 块将响应的IP地址和端口。默认情况下,任何不包含listen指令的 server 块默认 listen 在0.0.0.0:80(或者0.0.0.0:8080如果Nginx由普通的非root用户运行),这样的配置块响应80端口上任何接口的请求,但是这个默认值在server选择过程中没有太大的权重。

listen指令可以设置为:

  • IP地址/端口组合。
  • 只有IP地址,它将监听默认端口80。
  • 只有端口,它将监听该端口上的每个接口。
  • Unix套接字的路径。

最后的选项通常在不同的服务器之间传递请求时起到作用。

在尝试确定向哪个服务器块发送请求时,Nginx将首先尝试listen使用以下规则根据指令的特异性来决定:

  • Nginx用默认的缺省值来替换所有不完整的lesten指令(完整:IP+port的组合)的缺省值,因此每一个server块的listen指令都可以看作是IP地址和端口的组合。 这种转换的例子有:

    • 没有listen指令的块使用该值0.0.0.0:80
    • 设置为111.111.111.111没有端口的IP地址的块变为111.111.111.111:80
    • 设置为8888没有IP地址的端口的块变为0.0.0.0:8888
  • 接下来Nginx会尝试去收集一个server块的列表,这个列表是基于具体的IP和端口最佳匹配。也就是说如果匹配的server块有具体的IP地址,它就不会匹配用0.0.0.0作为默认的IP地址的server块。无论什么情况,在Nginx选择server块的过程中,端口必须准确匹配。

  • 如果只有一个最具体的匹配,那么该server块将用于提供请求。如果有多个server 块具有相同层次的具体匹配,那么Nginx需继续评估server_name指令 。

需要特别注意的是,只有 listen 指令在同一层次上有多个匹配的 server 块时,Nginx才会继续评估server_name指令。举个例子,如果域名example.com被解析到IP为192.168.1.10,端口为80的主机上,当客户端请求example.com时,在本例中,第一个server模块总是会提供服务,尽管server_name指令在第二个server模块中。

server{
listen 192.168.1.10;
....
}
server{
listen 80;
server_name example.com;
....
}

多个server模块在具体的匹配中处于同一级别的情况下,Nginx下一步才会检查server_name指令。

解析server_name指令选择一个匹配

接下来,为了进一步评估具有相同特定listen指令的请求,Nginx会检查请求的“host”标头,此值包含客户端实际尝试访问的域或IP地址。

Nginx在候选的每一个server模块中,查看其server_name指令,尝试去找到最佳的匹配。Nginx通过下面的公式来进行评估:

  • Nginx首先找到server_name与请求的Host头信息精准匹配的server模块,如果找到了这个server模块,它将会被用于服务客户端的请求。若有多个特定的匹配项被找到,第一个会被用于提供服务。
  • 如果没有找到精准的匹配项,Nginx接下来将尝试去找server_name与前置通配符(在配置中名称的开头用*表示)匹配的server模块。只要找到一个,这个server模块将被用于为客户端提供服务。如果找到了多个匹配,最长匹配结果的server模块将会被用于提供服务。
  • 如果使用前置通配符没有找到匹配时,Nginx接下来将尝试去找server_name与后置通配符(在配置中名称的结尾用*表示)匹配的server模块。只要找到一个,这个server模块将被用于为客户端提供服务。如果找到了多个匹配,最长匹配结果的server模块将会被用于提供服务。
  • 如果使用后置通配符没有找到匹配时,Nginx接下来将会评估用正则表达式(在名称前用~表示)定义server_name的server模块。带有与Host头匹配的正则表达式的第一个server_name将被用于提供服务。
  • 如果没有找到用正则表达式定义server_name的相匹配的server模块时,Nginx接下来会使用默认IP和端口的server模块。

每一个IP地址/端口组合都有一个默认的server模块,当用上面的方法不能确定一个操作的过程时将使用默认的server模块。对于IP地址/端口的组合来说,这将是配置中的第一个模块或者是包含default_server选项作为listen指令的一部分的server模块(这将复写first-found算法)。每一个IP地址/端口组合只能有一个default_server声明。

实例

如果已定义的server_name与Host头的值精准匹配时,这个server模块将被选择来处理请求。

在这个例子中,如果请求的Host头的值被设置为 host1.example.com,第二个server模块将被选中:

server{
listen 80;
server_name *.example.com;
...
}
server{
listen 80;
server_name host1.example.com;
...
}

如果精准的匹配没有被找到时,Nginx将会检查是否有一个具有适合前置通配符的server_name。以通配符开始的最长的server_name的server模块将会被选择来完成响应。

在这个例子中,如果请求的Host头是 www.example.org,第二个server模块将被选中:

server{
listen 80;
server_name www.example.*;
...
}
server{
listen 80;
server_name *.example.org;
...
}
server{
listen 80;
server_name *.org;
}

server_name以通配符开始的模块没有找到,Nginx将查看在表达式后面有通配符的匹配项是否存在。此时,以通配符结尾的最长的匹配项将被用于服务客户端的请求。

在这个例子中,如果请求的Host头被设置为 www.example.com,第三个模块将被选中:

server{
listen 80;
server_name host1.example.com;
...
}
server{
listen 80;
server_name example.com;
}
server{
listen 80;
server_name www.example.*;
}

如果通配符匹配项没有找到,Nginx将会去匹配用了正则表达式的server_name。第一个匹配上的server模块将会被选中来响应请求。

在这个例子中,如果请求的Host头设置为 www.example.com,那么第二个server模块将被选中来完成响应。

server{
listen 80;
server_name example.com;
...
}
server{
listen 80;
server_name ~^(www|host1).*\.example\.com$;
...
}
server{
listen 80;
server_name ~^(subdomain|set|www|host1).*\.example\.com$;
...
}

如果上述步骤都不能满足请求,则该请求将被传递到默认的server模块以获取匹配的IP地址和端口。