基于名称如何去选择虚拟服务
当我们配置多个server时,nginx是如何去决定由哪个server去处理请求的呢?让我们从一个简单的配置开始,以下是三个虚拟server在端口*:80上侦听:
server {
listen 80;
server_name example.org www.example.org;
...
}
server {
listen 80;
server_name example.net www.example.net;
...
}
server {
listen 80;
server_name example.com www.example.com;
...
}
在以上这个配置中,nginx会做如下处理:
1、Nginx只通过校验请求头“Host“去决定应该去路由到那个server。
2、如果一个请求的Host值没有匹配到任何的serve_name值,或者请求中没有包含“Host“请求头,那么nginx路由这个请求到指定port的默认server。
3、在上面的配置中,默认的Server是第一个(nginx默认的行为)。
当然我们也可以通过default_server参数(直接放在listen后面)去指定一个默认的server,如下:
server {
listen 80 default_server;
server_name example.net www.example.net;
...
}
tip:default_server参数在0.8.21开始可用,更早的版本中是使用default参数。
需要注意被default_server参数标记的server,但没有server_name数据,更多说明看下个章节。
在没有定义的server_name情况下如何去阻止处理请求
在nginx中如果我们不希望请求中没有Host请求头字段,可以定义一个server去丢弃这类请求,如下定义:
server {
listen 80;
server_name "";
return 444;
}
如上,server_name是设置空字符,将会匹配没有Host请求头字段的请求,并且指定了一个非标准http code 444去关闭连接。
自从0.8.48后,server_name默认值是空字符,因此如上server_name ““这个配置是可以省略掉的。在更早的版本中,机器的hostname被当作默认的server_name值。
基于名称和基于ip的混合虚拟服务
接下来我们来看一下一个混合的配置,每个server都监听不同的地址,如下:
server {
listen 192.168.1.1:80;
server_name example.org www.example.org;
...
}
server {
listen 192.168.1.1:80;
server_name example.net www.example.net;
...
}
server {
listen 192.168.1.2:80;
server_name example.com www.example.com;
...
}
在上面这个配置中,nginx会去做如下处理:
1、首先会去校验ip地址和端口是否和listen指定的一致。
2、listen匹配成功后再校验Host请求头是否与server_name指定的一致。
3、如果server_name没有匹配成功,请求将会被默认server处理。例如在192.168.1.1:80上收到一个来自www.example.com的请求,将会被192.168.1.1:80的默认server处理,即这第一个server(并且没有定义www.example.com server_name的服务)去处理这个请求。
如前面所说,defaul_server被放置在一个listen的port上,那么不同的默认server可能有不同的port上,如下配置:
server {
listen 192.168.1.1:80;
server_name example.org www.example.org;
...
}
server {
listen 192.168.1.1:80 default_server;
server_name example.net www.example.net;
...
}
server {
listen 192.168.1.2:80 default_server;
server_name example.com www.example.com;
...
}
一个简单的php站点配置
现在,让我们来看看nginx是如何去选择一个location去处理一个经典、简单的php站点的请求的,如下:
server {
listen 80;
server_name example.org www.example.org;
root /data/www;
location / {
index index.html index.php;
}
location ~* \.(gif|jpg|png)$ {
expires 30d;
}
location ~ \.php$ {
fastcgi_pass localhost:9000;
fastcgi_param SCRIPT_FILENAME
$document_root$fastcgi_script_name;
include fastcgi_params;
}
}
在上面配置中,nginx会去做如下处理。
1、nginx首先会去搜索由字面字符串的最特定的前缀location,并且会忽略location的顺序。在上面那个配置中,只有“/“前缀的location符合,因为它可以匹配任何请求,因此它将会被放在最后的排序。
2、之后nginx会按照在配置文件中的顺序去检查所有的location给定的规则。最先匹配的表达式后会停止查找,并且使用这个location。
3、如果没有匹配到任何表达式,那么nginx将会使用前面早先找到的最特定的location。
注意:所有的location值只匹配请求行的uri部分,排除所有的url参数,这样做是因为查询字符串的参数将会有多种形式,比如:
/index.php?user=john&page=1
/index.php?page=1&user=john
现在,让我们来看看nginx针对上面的配置是如何去处理请求的:
处理/logo.gif请求
1、请求首先是被“/“location匹配。
2、然后是被正则表达式 “\.(gif|jpg|png)$”匹配到,因此它会被后一个location处理。直接使用“root /data/www”去映射请求,映射到文件/data/www/logo.gif,最后将这个文件发给客户端。
处理/index.php请求
1、请求首先也是被“/“location匹配。
2、然后是被正则表达式 “\.(php)document_root等于根指令的值,变量$fastcgi_script_name等于请求的URI,即" /index.php "。
处理/about.html请求
1、首先请求只被“/“location匹配,因此将由“/“location去处理这个请求。
2、之后直接使用“root /data/www”去映射请求,映射到文件/data/www/about.html,最后将这个文件发给客户端。
处理一个“/“的请求
1、处理一个“/”是非常复杂的,它只被“/“location匹配,因此将由“/“location去处理这个请求。
2、然后会去通过“root /data/www“指令去检查index文件是否存在。如果/data/www/index.html存在,直接返回。
3、如果/data/www/index.html不存在,并且/data/www/index.php存在,那么将会重定向到“index.php“,然后nginx将这个请求当作是由客户端发送过来的,重新搜索所有的location。如前所述,这个重定向的请求最终将有FastCGI服务去处理
location优先级匹配
- =前缀的指令严格匹配这个查询。如果找到,停止搜索。
- 所有剩下的常规字符串,最长的匹配。如果这个匹配使用^〜前缀,搜索停止。
- 正则表达式,在配置文件中定义的顺序。
- 如果第3条规则产生匹配的话,结果被使用。否则,使用第2条规则的结果。
例如
location = / {
# 只匹配"/"
[ configuration A ]
}
location / {
# 匹配任何请求,因为所有请求都是以"/"开始
# 但是更长字符匹配或者正则表达式匹配会优先匹配
[ configuration B ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 开始的请求,并停止匹配其它location
[ configuration C ]
}
location ~* .(gif|jpg|jpeg)$ {
# 匹配以 gif、jpg或者jpeg结尾的请求
# 但是所有 /images/ 目录的请求将由 [Configuration C]处理
[ configuration D ]
}
请求URI例子:
-
/ -> 符合configuration A
-
/documents/document.html -> 符合configuration B
-
/images/1.gif -> 符合configuration C
-
/documents/1.jpg ->符合 configuration D
参考文档链接:nginx.org/en/docs/htt…