介绍
Nginx是世界上最流行的Web服务器之一。 它可以成功地处理高负载与许多并发客户端连接,并可以轻松地充当Web服务器,邮件服务器或反向代理服务器。
在本指南中,我们将讨论一些确定Nginx如何处理客户端请求的幕后细节。 了解这些想法可以帮助您在设计服务器和位置块时进行猜测,并且可以使请求处理看起来更不可预测。
Nginx模块配置
Nginx将用于服务不同内容的配置逻辑地划分为块,这些块存在于分层结构中。 每次做出客户端请求时,Nginx开始确定应该使用哪些配置块来处理请求的过程。 这个决策过程是我们将在本指南中讨论的。
我们将要讨论的主要模块是服务器块和块的位置 。
服务器块是Nginx配置的一个子集,定义用于处理定义类型的请求的虚拟服务器。 管理员通常配置多个服务器块,并根据请求的域名,端口和IP地址决定哪个块应处理哪个连接。
位置块驻留在服务器块中,用于定义Nginx应如何处理针对父服务器的不同资源和URI的请求。 URI空间可以被细分为管理员喜欢使用这些块的任何方式。 它是一个非常灵活的模型。
Nginx如何决定哪个服务器块将处理请求
由于Nginx允许管理员定义作为单独的虚拟web服务器实例的多个服务器块,它需要一个过程来确定这些服务器块中的哪些将被用于满足请求。
它通过一个用于找到最佳匹配的定义的检查系统来实现。 Nginx的是在此过程中涉及的主要服务器模块的指令是listen
指令,和server_name
指令。
解析“listen”指令以查找可能的匹配
首先,Nginx查看请求的IP地址和端口。 它匹配这对listen
每个服务器的指令,为建立能尽可能解决请求的服务器块的列表。
在listen
指令通常定义了IP地址和端口,服务器块将响应。 默认情况下,不包括任何服务器块listen
指令给出的参数听0.0.0.0:80
(或0.0.0.0:8080
如果Nginx的是由一个正常的,非root用户运行)。 这允许这些块响应端口80上任何接口上的请求,但是这个默认值在服务器选择过程中不具有很大的重要性。
在listen
指令可以设置为:
- IP地址/端口组合。
- 一个单独的IP地址,然后将在默认端口80上侦听。
- 一个单独的端口,将监听该端口上的每个接口。
- Unix套接字的路径。
最后一个选项通常只在不同服务器之间传递请求时有影响。
当试图确定哪个服务器块发送一个请求到,Nginx的将首先尝试基于所述的特异性决定listen
使用以下规则指令:
- nginx的翻译所有“不完全”
listen
通过使每个块可通过其IP地址和端口来评估他们的默认值代缺失值的指令。 这些翻译的一些示例是:- 没有一个块
listen
指令,使用值0.0.0.0:80
。 - 设置一个IP地址块
111.111.111.111
,没有端口将成为111.111.111.111:80
- 设置为端口块
8888
没有的IP地址成为0.0.0.0:8888
- 没有一个块
- Nginx然后尝试基于IP地址和端口最具体地收集与请求匹配的服务器块的列表。 这意味着,在功能上使用任何块
0.0.0.0
作为其IP地址(以匹配任何接口),如果有匹配的,列出的特定的IP地址块将不被选择。 在任何情况下,端口必须完全匹配。 - 如果只有一个最具体的匹配,那么该服务器块将用于服务请求。 如果有与特异性匹配的同一级别上的多个服务器块,Nginx的再开始,以评估
server_name
的每个服务器块的指令。
要明白的Nginx只会评价是很重要的server_name
时,它需要一个匹配服务器块之间进行区分,以在相同的水平的特异性的指令listen
指令。 例如,如果example.com
在端口托管80
的192.168.1.10
,对于一个请求example.com
将总是通过在本实施例的第一块,送达尽管server_name
中第二块指令。
server {
listen 192.168.1.10;
. . .
}
server {
listen 80;
server_name example.com;
. . .
}
在一个以上的服务器块具有相等的特异性相匹配的情况下,下一个步骤是检查server_name
指令。
解析“server_name”指令以选择匹配
接下来,以进一步评估有同样的具体要求listen
指令,Nginx的检查请求的“主机”标题。 此值保存客户端实际尝试访问的域或IP地址。
nginx的尝试找到它发现通过查看该值最佳匹配server_name
中的每个是仍然候选者的服务器块指令。 Nginx使用以下公式评估这些:
- Nginx的将首先尝试找到一个服务器块
server_name
,该值在完全相同的要求的“主机”头相匹配。 如果找到,相关的块将用于服务请求。 如果找到多个完全匹配, 第一个被使用。 - 如果没有找到精确匹配,Nginx的将尝试找到一个服务器块
server_name
的匹配使用通配符领先(由指示*
在名称在config开头)。 如果找到一个,该块将用于服务请求。 如果找到多个匹配, 最长匹配将被用来服务请求。 - 如果使用的是主导通配符发现不匹配,Nginx的然后查找与一个服务器块
server_name
进行匹配使用尾随通配符(通过与结尾的服务器名指示*
在config)。 如果找到一个,则该块用于服务请求。 如果找到多个匹配, 最长匹配将被用来服务请求。 - 如果使用通配符尾随发现不匹配,Nginx的再求定义服务器块
server_name
使用正则表达式(由指示~
名称之前)。 第一个server_name
正则表达式的“主机”头匹配将被用来服务请求。 - 如果没有找到正则表达式匹配,Nginx然后为该IP地址和端口选择默认服务器块。
每个IP地址/端口组合都有一个默认服务器块,当使用上述方法无法确定操作过程时,将使用该块。 一个IP地址/端口组合,这要么是在配置第一块或包含该块default_server
选项作为一部分listen
指令(这将重写第一发现算法)。 可以有只有一个default_server
按每个IP地址/端口组合声明。
例子
如果有一个server_name
定义完全“主机”头值相匹配时,该服务器块被选择来处理请求。
在此示例中,如果请求的“主机”标头设置为“host1.example.com”,则将选择第二个服务器:
server {
listen 80;
server_name *.example.com;
. . .
}
server {
listen 80;
server_name host1.example.com;
. . .
}
如果没有找到精确匹配,Nginx的再检查,看看是否有一个server_name
与适合起始通配符。 将选择以通配符开头的最长匹配项以满足请求。
在本实施例中,如果请求了的“一”主机“头www.example.org ”,第二个服务器块将被选择:
server {
listen 80;
server_name www.example.*;
. . .
}
server {
listen 80;
server_name *.example.org;
. . .
}
server {
listen 80;
server_name *.org;
. . .
}
如果找不到与起始通配符匹配,Nginx将使用表达式末尾的通配符查看是否存在匹配。 此时,将选择以通配符结尾的最长匹配项来提供请求。
例如,如果请求具有“主机”报头设置为“ 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
使用正则表达式指令。 第一个匹配的正则表达式将被选中的请求作出回应。
例如,如果“主机”的请求的首部设置为“ www.example.com ”,则第二服务器块将被选择,以满足该请求:
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$;
. . .
}
如果没有任何上述步骤都能够满足该请求,则该请求将被传递给用于匹配的IP地址和端口的默认服务器。
匹配位置块
与Nginx用于选择将处理请求的服务器块的过程类似,Nginx还具有确定的算法,用于决定服务器中的哪个位置块用于处理请求。
位置块语法
在我们介绍Nginx如何决定使用哪个位置块来处理请求之前,让我们来看一下你在位置块定义中可能看到的一些语法。 位置块位于服务器块(或其他位置块)内,用于决定如何处理请求URI(域名或IP地址/端口之后的请求部分)。
位置块通常采取以下形式:
location optional_modifier location_match {
. . .
}
该location_match
在上面定义了Nginx的应检查请求URI反对。 上面示例中修饰符的存在或不存在影响Nginx尝试匹配位置块的方式。 下面的修改将导致相关的位置块被解释如下:
- (无):如果没有调节剂存在时,位置被解释为一个前缀匹配。 这意味着给定的位置将匹配请求URI的开始以确定匹配。
-
=
:如果使用一个等号,该模块将被视为匹配,如果请求的URI给定的位置完全匹配。 -
~
:如果一个波浪线修饰符,这个位置将被解释为区分大小写的正则表达式匹配。 -
~*
:如果使用了波浪线和星号修改,定位板将被解释为不区分大小写的正则表达式匹配。 -
^~
如果克拉和波浪修饰符,如果此块被选为最佳非正则表达式匹配,正则表达式匹配将不会发生。
演示位置块语法的示例
作为前缀匹配的一个例子,下面的位置的块可被选择为对于像URI请求响应/site
, /site/page1/index.html
或/site/index.html
:
location /site {
. . .
}
对于确切的请求的URI匹配的演示,此块将始终用来为请求URI看起来像响应/page1
。 它不会被用来向一个响应/page1/index.html
请求URI。 请记住,如果选择此块,并且使用索引页完成请求,则内部重定向将发生到另一个位置,该位置将是请求的实际处理程序:
location = /page1 {
. . .
}
作为应该被解释为,区分大小写的正则表达式的位置的一个例子,该块可用于处理对请求/tortoise.jpg
,但不适用于/FLOWER.PNG
:
location ~ \.(jpe?g|png|gif|ico)$ {
. . .
}
将允许类似于上述的不区分大小写匹配的块如下所示。 在这里,无论/tortoise.jpg
和 /FLOWER.PNG
可以通过此块来处理:
location ~* \.(jpe?g|png|gif|ico)$ {
. . .
}
最后,如果确定该块是最佳的非正则表达式匹配,则该块将阻止正则表达式匹配发生。 它可以处理的请求/costumes/ninja.html
:
location ^~ /costumes {
. . .
}
如您所见,修饰符指示应如何解释位置块。 然而,这并没有告诉我们,Nginx的使用来决定哪些位置块发送请求的算法。 接下来我们将讨论。
Nginx如何选择使用哪个位置来处理请求
Nginx以类似于如何选择服务器块的方式选择将用于服务请求的位置。 它运行通过一个过程,确定任何给定请求的最佳位置块。 理解这个过程是能够可靠和准确地配置Nginx的关键要求。
记住我们上面描述的位置声明的类型,Nginx通过比较请求URI到每个位置来评估可能的位置上下文。 它使用以下算法执行此操作:
- Nginx首先检查所有基于前缀的位置匹配(所有位置类型不涉及正则表达式)。 它根据完整的请求URI检查每个位置。
- 首先,Nginx寻找一个完全匹配。 如果使用的是一个位置块
=
发现修改完全匹配的请求的URI,这个位置块被立即选择服务请求。 - 如果没有确切的(与
=
修饰符)的位置块匹配被发现,Nginx的然后移动到评估非精确的前缀。 它发现给定请求URI的最长匹配前缀位置,然后它评估如下:- 如果最长匹配前缀的位置有
^~
修饰符,那么Nginx的将立即结束其搜索并选择该位置服务请求。 - 如果最长匹配前缀位置不使用
^~
改性剂,所述匹配的时刻,以便搜索的焦点可移动存储由Nginx的。
- 如果最长匹配前缀的位置有
- 在确定和存储最长匹配前缀位置之后,Nginx继续评估正则表达式位置(区分大小写和不区分大小写)。 如果有最长匹配前缀位置内的任何正则表达式的位置,Nginx的将移动那些到其正则表达式的位置的列表的顶部检查。 Nginx然后尝试顺序地匹配正则表达式位置。 该请求URI匹配的第一个正则表达式的位置立即选择服务请求。
- 如果没有找到与请求URI匹配的正则表达式位置,则选择先前存储的前缀位置来服务该请求。
重要的是要理解,默认情况下,Nginx将服务正则表达式匹配优先于前缀匹配。 然而,它首先计算前缀位置,从而允许辖通过指定使用位置以覆盖此倾向=
和^~
改性剂。
还重要的是注意,尽管前缀位置通常基于最长的最特定匹配来选择,但是当找到第一匹配位置时,停止正则表达式求值。 这意味着在配置中的定位对正则表达式位置有很大的影响。
最后,要明白,正则表达式的最长前缀匹配内的匹配,将“跳行”时,Nginx的正则表达式进行评估它的位置是很重要的。 在考虑任何其他正则表达式匹配之前,将按顺序对它们进行求值。 马克西姆Dounin,一个令人难以置信的有益Nginx的开发者,在解释这一职位的选择算法的这一部分。
什么时候进行位置块评估跳到其他位置?
一般来说,当选择一个位置块来服务一个请求时,该请求完全在该上下文中从该点向前处理。 只有选定的位置和继承的指令确定请求的处理方式,而不受兄弟位置块的干扰。
虽然这是一个通用规则,将允许您以可预测的方式设计位置块,但重要的是要意识到有时,新的位置搜索由所选位置内的某些指令触发。 “只有一个位置块”规则的例外可能会影响请求的实际提供方式,并可能与您在设计位置块时的期望不一致。
可能导致这种类型的内部重定向的一些指令是:
- 指数
- try_files
- 改写
- error_page
让我们略过这些。
的index
,如果它是用来处理所述请求指令总是导致内部重定向。 精确位置匹配通常用于通过立即结束算法的执行来加速选择过程。 但是,如果你做一个精确的位置匹配的是一个目录 ,有一个很好的机会,请求将被重定向到一个不同的位置进行实际的处理。
在本实施例中,第一位置是由一个请求的URI相匹配/exact
,但为了处理请求,所述index
由块继承指令启动一个内部重定向到第二块:
index index.html;
location = /exact {
. . .
}
location / {
. . .
}
在上面的情况下,如果你真的需要执行留在第一个块,你将不得不提出一种不同的方法来满足目录的请求。 例如,你可以设置一个无效的index
为块,并打开autoindex
:
location = /exact {
index nothing_will_match;
autoindex on;
}
location / {
. . .
}
这是防止的一种途径index
从上下文切换,但它可能不适合大多数配置非常有用。 大多数目录上的精确匹配对于重写请求(这也导致新的位置搜索)等事情有帮助。
其中,所述处理位置可以重新评估的另一个实例是与try_files
指令。 这个指令告诉Nginx检查一个命名的文件或目录集的存在。 最后一个参数可以是Nginx将进行内部重定向的URI。
考虑以下配置:
root /var/www/main;
location / {
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
在上面的例子中,如果请求是对于由/blahblah
,所述第一位置将首先获得请求。 它会尝试找到一个名为blahblah
中/var/www/main
目录。 如果不能找到一个,它会通过搜索一个名为跟进blahblah.html
。 然后,它会尝试看看是否有一个名为blahblah/
的范围内/var/www/main
目录。 失败的所有这些尝试,它会重定向到/fallback/index.html
。 这将触发另一个位置搜索,将被第二个位置块捕获。 这将有助于该文件/var/www/another/fallback/index.html
。
这可能会导致一个位置块冒充另一个指令是rewrite
指令。 当使用last
参数与rewrite
指令,或使用任何参数时在所有,Nginx的将搜索基于所述重写的结果的新的匹配位置。
例如,如果我们修改最后的例子为包括重写,我们可以看到,该请求被有时直接传递到所述第二位置,而不依赖于try_files
指令:
root /var/www/main;
location / {
rewrite ^/rewriteme/(.*)$ /$1 last;
try_files $uri $uri.html $uri/ /fallback/index.html;
}
location /fallback {
root /var/www/another;
}
在上面的例子中,对于一个请求/rewriteme/hello
将最初由第一位置块处理。 它将改写为/hello
和位置将被搜索。 在这种情况下,将再次匹配第一位置,并通过该处理try_files
照常,也许踢回/fallback/index.html
如果没有找到(利用try_files
我们上面讨论的内部重定向)。
但是,如果请求的是制作/rewriteme/fallback/hello
,首块将再次匹配。 重写再次应用,导致这段时间/fallback/hello
。 然后,将从第二位置块中提供该请求。
一个相关的情况将发生在return
发送指令时, 301
或302
状态码。 在这种情况下的区别是,它导致一个全新的请求以外部可见的重定向的形式。 这种相同的情况下可以用发生rewrite
使用时,指令redirect
或permanent
标志。 但是,这些位置搜索不应该是意外的,因为外部可见的重定向总是产生一个新的请求。
该error_page
指令可导致类似于由创建内部重定向try_files
。 此伪指令用于定义在遇到某些状态代码时应该发生什么。 如果这将有可能永远不会被执行try_files
设置,因为该指令处理请求的整个生命周期。
考虑这个例子:
root /var/www/main;
location / {
error_page 404 /another/whoops.html;
}
location /another {
root /var/www;
}
每一个请求(除了那些开始与其他/another
)将第一个块,这将成为文件出来进行处理/var/www/main
。 但是,如果没有找到一个文件(404状态),内部重定向/another/whoops.html
会发生,导致一个新的位置搜索将在第二块最终登陆。 这个文件将被提供出来/var/www/another/whoops.html
。
如你所见,理解Nginx触发新位置搜索的情况有助于预测在发出请求时您将看到的行为。
结论
理解Nginx处理客户端请求的方式可以使您作为管理员的工作更容易。 您将能够知道Nginx将根据每个客户端请求选择哪个服务器块。 您还可以根据请求URI了解如何选择位置块。 总的来说,知道Nginx选择不同块的方式将使您能够跟踪Nginx将应用的上下文以便为每个请求提供服务。