背景:
前面简单的说了如何配置nginx做虚拟主机,我们知道定义一个虚拟主机需要先配置server块去匹配访问的URL地址中的域名,然后在配置location块,匹配访问的URL地址中的URI,下面就展开说说Server块和location。
Server解析
1. Server是什么?
前面我们一直说nginx配置虚拟主机,严格意义上就是配置一个server块,一个server块就定义了一个web服务。当前端通过URL来访问web服务的时候,nginx通过域名来匹配有哪个server块处理请求。即server是用来匹配URL中的域名,从而判断有哪个server块来处理请求。
2. Server块解析
2.1 让我们从一个简单的配置开始,以下三个虚拟服务器都侦听端口 *:80
server {
## 设置监听端口
listen 80;
## 设置URL的域名匹配
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 通过server块中的server_name参数来测试请求的标头字段“Host”,以确定应将请求路由到哪个服务器。如果它的值不匹配任何服务器名称,或者请求根本不包含这个头域,那么 nginx 会将请求路由到这个端口的默认服务器。
在上面的配置中,默认服务器是第一个——这是 nginx 的标准默认行为。也可以使用listen指令中的default_server参数明确设置哪个服务器应该是默认的:
server {
listen 80 default_server;
server_name example.net www.example.net;
...
}
如果不允许没有“Host”头字段的请求,可以定义一个只丢弃请求的服务器:
server {
listen 80;
server_name "";
return 444;
}
在这里,服务器名称设置为一个空字符串,它将匹配没有“Host”头字段的请求,并返回一个特殊的 nginx 的非标准代码 444 来关闭连接。
2.2 看一个更复杂的配置,其中一些虚拟服务器侦听不同的地址:
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 首先根据服务器块的侦听指令测试请求的 IP 地址和端口。然后,它根据与 IP 地址和端口匹配的服务器块的 server_name 条目匹配测试请求的“Host”标头字段。如果未找到服务器名称,则请求将由默认服务器处理。例如,在 192.168.1.1:80 端口收到的对 www.example.com 的请求将由 192.168.1.1:80 端口的默认服务器处理,即由第一台服务器处理(没有设置listen decault_server),因为没有 www.example .com 为该端口定义。
默认服务器是监听端口的一个属性,并且可以为不同的端口定义不同的默认服务器
3. 配置示例
定义两个server块,其中第二个server块中配置listen default_server
server {
listen 80;
server_name xhz.com,www.xhz.com;
access_log /var/log/nginx/host.access.log main;
autoindex on;
location / {
root /nginx/xhz;
index index.html;
}
}
server {
listen 80 default_server;
server_name flf.com,www.flf.com;
access_log /var/log/nginx/host.access.log main;
autoindex on;
location / {
root /nginx/flf;
index index.html;
}
}
1)通过www.xhz.com访问时
2)通过www.flf.com访问时
3) 通过www.xjl.com访问时,此时匹配不到server_name
Location解析
1. 什么是Location?
location是用来控制URI的访问路径的。当访问的URL中的域名与server块中的sererv_name匹配之后,将请求转发到相应的服务器块,随后进行URL中的URI部分匹配,由于server块中可以有多个location,从而确定由哪个location进行处理。
2. Location的匹配规则
2.1 语法
location [ = | ~ | ~* | ^~ ] uri {
...
}
location @name {
...
}
2.2 匹配规则
位置可以由前缀字符串或正则表达式定义。正则表达式使用前面的“~*”修饰符(用于不区分大小写的匹配)或“~”修饰符(用于区分大小写的匹配)指定。为了找到与给定请求匹配的位置,nginx 首先检查使用前缀字符串(前缀位置)定义的位置。其中,匹配前缀最长的位置被选中并记忆。然后按照它们在按照配置文件中出现的顺序检查正则表达式。正则表达式的搜索在第一次匹配时终止,并使用相应的配置。如果找不到与正则表达式的匹配项,则使用前面记住的前缀位置的配置。
即:1)先匹配前缀最长,童通俗的说就是匹配度最高的 location;2)然后再按照配置文件中出现的顺序检查正则表达式。正则表达式的搜索在第一次匹配时终止,并使用相应的配置。如果找不到与正则表达式的匹配项,则使用前面记住的最长匹配
规则 | 用法 | 优先级 |
= | 精准匹配 | 1 |
^~ | 以某个字符串开头 | 2 |
~ | 区分大小写的正则匹配 | 3 |
/ | 通用匹配,任何请求都会匹配到 | 4 |
~* | 不区分大小写的正则匹配 | 5 |
2.3 匹配示例
location = / {
# 只精准匹配 / 的查询.
[ configuration A ]
}
# 匹配成功: /
location / {
# 匹配任何请求,因为所有请求都是以”/“开始
# 但是更长字符匹配或者正则表达式匹配会优先匹配
[ configuration B ]
}
#匹配成功:/index.html
location /documents {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索/
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条/
[ configuration C ]
}
# 匹配成功:/documents/document.html
# 匹配成功:/documents/abc
location ~ /documents/ABC {
# 区分大小写的正则匹配
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索/
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条/
[ configuration CC ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,立即停止往下搜索正则,采用这一条。/
[ configuration D ]
}
# 成功匹配:/images/a.gif
location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 .gif、.jpg 或 .jpeg 结尾的请求,不区分大小写
# 然而,所有请求 /images/ 下的图片会被 [ config D ] 处理,因为 ^~ 到达不了这一条正则/
[ configuration E ]
}
# 成功匹配:/documents/a.jpg
location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在/
[ configuration F ]
}
location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在/
# F与G的放置顺序是没有关系的/
[ configuration G ]
}
location ~ /images/abc/ {
# 只有去掉 [ config D ] 才有效:先最长匹配 [ config G ] 开头的地址,继续往下搜索,匹配到这一条正则,采用/
[ configuration H ]
}
“/”请求将匹配配置 A,“/index.html”请求将匹配配置 B,“/documents/document.html”请求将匹配配置 C,“/images/1.gif”请求将匹配配置 D,“/documents/1.jpg”请求将匹配配置 E。
2.3 命名Location
带有 @ 的 location 是用来定义一个命名的 location,这种 location 不参与请求匹配,一般用在内部定向。
location / {
try_files $uri $uri/ @custom
}
location @custom {
# ...do something
}
## 分析try_files $uri $uri/ @custom:
1. 假设我定义的 root 为 /usr/share/nginx/html/,访问的 URI 是 /hello/xhz
2. 当 URI 被匹配后,会先查找 /usr/share/nginx/html//hello/shiyanlou 这个文件是否存在,如果存在则返回。
3.查找 /usr/share/nginx/html//hello/xhz/ 目录是否存在,如果存在,按 index 指定的文件名进行查找,比如 index.html,如果存在则返回。
4. 上面都不存在的时候,则会执行location @custom 中定义的内容。
2.4 location URI结尾带不带 /
对于请求URI结尾是否带有/,一般的处理逻辑是带/表示访问目录,不带/表示访问文件,如果文件不存在也会去匹配目录。如访问http://www.nginx.cn/images/和http://www.nginx.cn/images,前面的请求会匹配目录,后面的请求会先匹配文件,文件不存再匹配目录
对于locatioin中的URI来说,如果URI的结尾带有/,并且location要执行的命令式是proxy_pass、fastcgi_pass、uwsgi_pass、scgi_pass、memcached_pass、grpc_pass之一。
location /user/ {
proxy_pass http://user.example.com;
}
对于这种情况,nginx
会做特殊处理,不管user
命名的文件或目录存在不在,如果你访问http://www.nginx.cn/user
会被重定向到http://www.nginx.cn/user/
。
因此想这两种请求对应不同的处理,就要明确增加不带/
结尾的location
配置,如下
location /user/ {
proxy_pass http://user.example.com;
}
location = /user {
proxy_pass http://login.example.com;
}
示例:
server {
listen 80;
server_name user.com www.user.com;
access_log /var/log/nginx/host.access.log main;
autoindex on;
location /user {
root /nginx/user;
index index.html;
}
location = /user/ {
root /nginx/user;
index index.html;
}
}
## 返回结果
## 返回301是因为/nginx/user目录下没有user的文件
C:\Users\xhz>curl www.user.com/user
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.22.0</center>
</body>
</html>
C:\Users\xhz>curl www.user.com/user/
user test
##备注
- 配置中的字符有没有 / 都没有影响。也就是说 /user/ 和 /user 是一样的。
- 如果 URI 结构是 https://xhz.com/ 的形式,尾部有没有 / 都不会造成重定向。因为浏览器在发起请求的时候,默认加上了 / 。虽然很多浏览器在地址栏里也不会显示 /
- 如果 URI 的结构是 https://xhz.com/user/ 。尾部如果缺少 / 将导致重定向。因为根据约定,URL 尾部的 / 表示目录,没有 / 表示文件。所以访问 /user/ 时,服务器会自动去该目录下找对应的默认文件。如果访问 /user 的话,服务器会先去找 user 文件,找不到的话会将 user 当成目录,重定向到 /user/ ,去该目录下找默认文件。
2.4 Location的访问控制
nginx的deny和allow指令是由ngx_http_access_module模块提供,Nginx安装默认内置了该模块。
语法:
语法:allow/deny address | CIDR | unix: | all
它表示,允许/拒绝某个ip或者一个ip段访问.如果指定unix:,那将允许socket的访问。
注意:unix在1.5.1中新加入的功能。
在nginx中,allow和deny的规则是按顺序(从上至下)执行的,只要有一个匹配上就不会向下匹配。
示例:
示例1:
location /
{
allow 192.168.0.0/24;
allow 127.0.0.1;
deny all;
}
说明:这段配置值允许192.168.0.0/24网段和127.0.0.1的请求,其他来源IP全部拒绝。
示例2:
location = /user/ {
root /nginx/user;
index index.html;
allow all;
deny 192.168.65.1;
}
说明:当192.168.65.1访问时由于allow all已经匹配上允许所有,所以不会在向下匹配。
后续即将说到的nginx配置反向代理和负载均衡等功能,实际都是通过配置location来实现相应的功能,因此我们熟练掌握location的匹配规则。