存档

‘nginx’ 分类的存档

nginx proxy_pass 指令结尾加不加/的区别

2020年3月10日 没有评论

If it is necessary to transmit URI in the unprocessed form then directive proxy_pass should be used without URI part.

这段话的意思是如果你要保持输入的请求原样不变传给后端,那么你的proxy_pass指令就不能带有URI部分。

读起来比较晦涩,尤其是英文长句,举个实际例子:

#结尾不加/
 location /abc/ {
        proxy_pass http://10.1.12.123:8080;
 } 
#结尾添加/
location /abc/ {
        proxy_pass http://10.1.12.123:8080/;
 }

咋一看,这两个配置没什么区别。实际上最主要的区别就是proxy_pass指令后边有没有“/”,这部分内容叫做URI。

当请求是http://www.redis.com.cn/about/123时:

第一个配置处理之后传给后端应用的请求是 http://www.redis.com.cn/about/123 不变。

第二个配置会把请求 http://www.redis.com.cn/about/123 中与 location 匹配的部分("/abc/")替换为proxy_pass结尾的URI( "/" ),变成 http://www.redis.com.cn/123

分类: nginx 标签:

php-fpm安装tidy扩展

2020年3月8日 没有评论

W3tc是一个wordpress速度优化插件,插件的运行需要很多php扩展,其中对html的优化需要用到tidy。

安装tidy库

yum install libtidy libtidy-devel -y

源码编译安装,进入php-fpm的源码路径

cd php-7.3.11/ext/tidy/

编译和安装tidy扩展库

/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

编辑php.ini

[root@VM_0_16_centos tidy]# vi /usr/local/php/etc/php.ini

去掉tidy.so前的分号

;extension=soap
;extension=sockets
;extension=sodium
;extension=sqlite3
extension=tidy
;extension=xmlrpc
;extension=xsl

重启php-fpm生效

分类: nginx, php 标签: ,

一文彻底读懂nginx中的location指令

2020年3月6日 没有评论

location指令是nginx中最关键的指令之一,location指令的功能是用来匹配不同的url请求,进而对请求做不同的处理和响应,这其中较难理解的是多个location的匹配顺序,本文会作为重点来解释和说明。

开始之前先明确一些约定,我们输入的网址叫做请求URI,nginx用请求URI与location中配置的URI做匹配。

location格式

location有两种格式:

  • 匹配uri类型,有四种参数可选,当然也可以不带参数。
  • 命名location,用@来标识,类似于定义goto语句块。

location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }02

location匹配参数解释

参数
解释

location后没有参数直接跟着URI,表示前缀匹配,代表跟请求中的URI从头开始匹配。
~执行一个正则匹配,区分大小写。
~*执行一个正则匹配,不区分大小写。
^~普通字符匹配,多用来匹配目录。
=执行普通字符精确匹配。
@"@" 定义一个命名的 location,@定义的locaiton名字一般用在内部定向,例如error_page, try_files命令中。它的功能类似于编程中的goto。

location匹配顺序

nginx有两层指令来匹配请求URI。第一个层次是server指令,它通过域名、ip和端口来做第一层级匹配,当找到匹配的server后就进入此server的location匹配。location的匹配并不完全按照它们在配置文件中出现的顺序来匹配,请求URI会按如下规则跟server里配置的location匹配。

  1. 寻找有没有“=”等号参数完全匹配的location,如果有完全匹配的等号location则停止匹配,执行该location中的指令,不去匹配其它类型的location。
  2. 匹配所有非正则表达式URI的location(包括空,=,^~三种参数)。找到请求URI和location URI按前缀匹配最长的location,如果这个最长的location的参数是^~,则停止匹配,执行该location中的指令,否则暂存该location。
  3. 匹配正则表达式URI的location(包括~,~*两种参数),按location在配置文件中出现的顺序匹配,如果找到第一个匹配的locaiton则停止匹配,执行该location。
  4. 匹配完所有正则表达式都没有匹配的location,则执行第二步中暂存的最长前缀匹配location。

简单来说按这个规则:
= > ~^ > ~ = ~* >最长前缀匹配 > /

匹配问号后的参数

请求URI中问号后面的参数是不能在location中匹配到的,这些参数存储在$query_string变量中,可以用if来判断。

例如,对于参数中带有单引号'进行匹配然后重定向到错误页面。

/plus/list.php?tid=19&mid=1124'

if ( $query_string ~* ".*[;'<>].*" ){
return 404;
}

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  /images/ {
proxy_pass http://www.redis.com.cn
}

对于这种情况,nginx会做特殊处理,不管images命名的文件或目录存在不在,如果你访问http://www.nginx.cn/images会被重定向到http://www.nginx.cn/images/。

所以如果你想这两种请求对应不同的处理,就要明确增加不带/结尾的location配置。

location  /images {
proxy_pass http://www.rabbitmq.cn
}
location /images/ {
proxy_pass http://www.redis.com.cn
}

命名location

带有"@"的location是用来定义一个命名的location,这种location不参与请求匹配,一般用在内部定向。例如用在error_page, try_files命令中。它的功能类似于编程中的goto。

location  /images {
try_files $uri $uri/ @name;
}
location @name {
...
}

例子

location  = / {
  # 只匹配请求 "/"
  [ configuration A ] 
}
location  / {
  # 匹配任何请求,因为所有请求都是以"/"开始
  # 但是更长字符匹配或者正则表达式匹配会优先匹配
  [ configuration B ] 
}
location /documents/ {
  # 匹配所有 /documents/ 开头的请求,在没有正则表达
  # 式匹配时选择该locaiton
  [ configuration C ]
}
location ^~ /images/ {
  # 匹配任何以 /images/ 开始的请求,并停止匹配其它location
  [ configuration D ] 
}E
location ~* .(gif|jpg|jpeg)$ {
  # 匹配以 gif, jpg, or jpeg结尾的请求. 
  # 但是所有 /images/ 目录的请求将由 [Configuration D]处理.   
  [ configuration E ] 
}

请求URI例子:

  1. / -> 匹配A
  2. /index.html -> 匹配B
  3. /documents/a.html -> 匹配C
  4. /images/1.gif -> 匹配D
  5. /documents/1.jpg -> 匹配E

不知道有没有解释不清楚的地方,希望没把大家带沟里去。

留言告诉我吧。

分类: nginx 标签:

nginx版本号详解

2020年3月5日 没有评论

nginx 1.17 已发布,这是 nginx 目前最新的版本,同时也是 mainline 主线版。

我们都知道,nginx 都会同时维护着两个分支,分别为 mainline 主线版和 stable 稳定版。

  • mainline 是更新活跃的开发分支,其添加了最新功能和错误修复。其版本号的第二位用奇数表示,例如 1.17.0。
  • stable 接收针对高严重性错误的修复,但不会使用最新的功能。其版本号的第二位用偶数表示,例如 1.16.0。

不过在 nginx 中,“稳定”指的是功能和更新频率,它与软件质量无关。稳定分支在其生命周期中从不接收新功能,并且通常仅接收一个或两个更新,用于修复严重的错误。

稳定分支的生命周期一般是一年,每年四月官方就会停止对当前稳定分支的维护,不再提供错误修复补丁。这会触发两个事件:

  • 第一,官方会 fork 当前的 mainline 版本,以用于创建下一个稳定版本。这样一来,稳定分支就继承了过去一年中 mainline 版本的所有错误修复补丁、新增功能和其他变更。上个月发布的 nginx 1.16.0 稳定版就是 fork 自 1.15.12 mainline 版本
  • 第二,mainline 分支将会获得新的版本升级,即其版本号的第二位奇数会增加为下一个奇数,mainline 分支每 4-6 周就会发布一个新版本,nginx 1.17.0 是最新 mainline 版本的首次发布。

▲ 看图比较容易理解 nginx 版本的演进过程

生成环境应该选择nginx哪个版本

官方还建议用户采用主线分支,因为它能最快地用上新特性、性能改进和增强功能,还可以及时修复错误。总之,按官方的说法,主线分支完全适用于生产环境。

下面看看作为最新 mainline 版本的 1.17.0,包含了哪些新功能和改进。

nginx 1.17.0 包含对带有limit_rate指令的带宽限制配置中的变量的支持,并且允许该include指令在所有配置上下文中使用,甚至在if块内。

开发团队还开始支持 QUIC 和 HTTP/3(传输协议的下一次重大更新版本),它将应用到网站、应用程序和 API。

其他变更包括:

  • 支持的最低 OpenSSL 版本为 0.9.8
  • include 指令在“if”和“limit_except”块中不起作用
  • ……

完整内容点此查看

来源: https://www.oschina.net/news/106928/nginx-1-16-1-17-released

分类: nginx 标签:

Nginx中的if指令详解

2020年3月4日 没有评论

if指令用来判断条件为true时要执行的指令,条件false时不执行相应的指令,if指令只能用在server、location内。

01

if指令的格式如下:

if (condition) { ... }

condition可以是如下类型:

  • 变量名,如果变量的值是空字符串或者0表示false
  • 变量使用“=”和“!=”来跟字符串比较
  • 可以是正则表达式
  • 检查文件是否存在使用“-f” 和 “!-f”
  • 检查目录是否存在使用 “-d” 和 “!-d”
  • 检查文件、目录、符号链接是否存在使用 “-e” 和 “!-e”
  • 检查是否是可执行文件使用“-x” 和 “!-x”

if指令不支持多条件、不支持嵌套、不支持else,与常见的条件表达式不同的是if指令等值比较使用单个等号=而不是双等号==。

02

if是一个危险的指令,除了当判断一个变量的值时必须使用if的场景,其它情况使用if时都需要仔细思考可能会出现的执行结果,能不用就不用,稍不小心就会有意想不到的执行结果。我们大多数人都按过程化顺序编程思路来判断结果,但nginx还有很多上下文环境需要考虑。

比较安全的用法是满足if条件时只执行return指令。{}内尽量只使用下面两种指令。否则,nginx可能不会按照你预想顺序执行,严重的情况下nginx会段错误崩溃。

  • return ...;
  • rewrite ... last;

还有就是尽量别在location中使用if,在server中使用if相对会安全些。

03

如果要在location中使用if,最好用如下模版格式:

location / {
    error_page 418 = @other;
    recursive_error_pages on;
    if ($something) {
        return 418;
    }
    # some configuration
    ...
}
location @other {
    # some other configuration
    ...
}

04

nginx不支持下面这样的多条件if:

if ($request_method = POST && $http_cookie ~* "CCCC=.+(?:;|$)")

变通办法是:

if ($request_method = POST) {
    set $test  P;
  }
  if ($http_cookie ~* "CCCC=.+(?:;|$)" ) {
    set $test  "${test}C";
  }
  if ($test = PC) {
    #rewrite rule goes here.
  }

最后,有想深入了解if指令运行原理的同学,可以参考https://agentzh.blogspot.com/2011/03/how-nginx-location-if-works.html

分类: nginx 标签:

nginx禁止未绑定https域名访问

2020年2月19日 没有评论

http的情况系下,我们使用如下办法禁止未授权的域名指向我们的服务器

 server {
    listen       80  default_server;
    server_name  _;
    return       444;
} 

如何把端口简单改成443,会宝如下错误

[root@VM_0_16_centos conf]# nginx -t
 nginx: [emerg] no "ssl_certificate" is defined for the "listen … ssl" directive in /usr/local/nginx/conf/nginx.conf:105
 nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

在我们未设置默认server的情况下,nginx会匹配配置中的第一个server来响应非自己绑定的域名。

这种情况下需要自己自定义一个签名证书来解决这个问题。

1.nginx的配置修改改为

server {
     listen 443 ssl;
     server_name _;
     ssl_certificate /usr/local/nginx/conf/ssl/default.crt;
     ssl_certificate_key /usr/local/nginx/conf/ssl/default.key;
     return       444;
 }

2.创建自定义的key

[root@VM_0_16_centos conf]# sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/nginx/conf/ssl/default.key -out /usr/local/nginx/conf/ssl/default.crt
 Generating a 2048 bit RSA private key
 ………………………………………..+++
 ……………………+++
 writing new private key to '/usr/local/nginx/conf/ssl/default.key'
 You are about to be asked to enter information that will be incorporated
 into your certificate request.
 What you are about to enter is what is called a Distinguished Name or a DN.
 There are quite a few fields but you can leave some blank
 For some fields there will be a default value,
 If you enter '.', the field will be left blank.
 Country Name (2 letter code) [XX]:cn
 State or Province Name (full name) []:beijing
 Locality Name (eg, city) [Default City]:beijing
 Organization Name (eg, company) [Default Company Ltd]:default
 Organizational Unit Name (eg, section) []:default
 Common Name (eg, your name or your server's hostname) []:default
 Email Address []:default

3.重新加载配置

[root@VM_0_16_centos conf]# nginx -t
 nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
 nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
 [root@VM_0_16_centos conf]# nginx -s reload

80 443合并的例子

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_tickets off;

    return 404;
}

 

分类: nginx 标签:

nginx proxy_pass 配置详解

2019年7月10日 没有评论
语法:proxy_pass URL;
默认值:
上下文:locationif in locationlimit_except

设置被代理的服务器的协议和地址,还可以设置可选的URI。

协议是“http”或者“https”。

地址既可以使用域名或者IP地址加端口(可选)的形式来定义:

proxy_pass http://localhost:8000/uri/;

或使用UNIX域套接字路径来定义。该路径接在“unix”字符串后面,两端由冒号所包围,比如:

proxy_pass http://unix:/tmp/backend.socket:/uri/;

如果解析一个域名得到多个地址,所有的地址都会以轮转的方式被使用。当然,也可以使用upstream来定义地址。

请求URI按下面规则传送给后端被代理服务器:

1.如果proxy_pass使用了URI(下面例子中127.0.0.1地址后面部分,包括只有斜杠的情况),请求路径与loction路径的匹配部分将被替换为proxy_pass中定义的URI:

 
location /name/ {
proxy_pass http://127.0.0.1/remote/;
}

2.如果proxy_pass没有使用URI,发给被代理服务器的请求路径和客户端发情的请求路径相同,不会被修改。

location /some/path/ {
proxy_pass http://127.0.0.1;
}

特殊情况:

1.location使用正则表达式定义路径。这种情况下,指令不应该带有URI。

2.使用rewrite指令改变了URI,但仍使用相同配置处理请求(break):

location /name/ {
rewrite /name/([^/]+) /users?name=$1 break;
proxy_pass http://127.0.0.1;
}

这种情况下,指令设置的URI会被忽略,改变后的URI将被发送给后端服务器。

3.后端服务器的地址,端口和URI中都可以使用变量:

proxy_pass http://$host$uri; 
分类: nginx 标签:

nginx upstream 配置和作用

2019年7月10日 没有评论

配置例子

upstream backend {
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

指令

语法:upstream name { ... }
默认值:
上下文:http

定义一组服务器。 这些服务器可以监听不同的端口。 而且,监听在TCP和UNIX域套接字的服务器可以混用。

例子:

upstream backend {
    server backend1.example.com weight=5;
    server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;
    server unix:/tmp/backend3;
}

默认情况下,nginx按加权轮转的方式将请求分发到各服务器。 在上面的例子中,每7个请求会通过以下方式分发: 5个请求分到backend1.example.com, 一个请求分到第二个服务器,一个请求分到第三个服务器。 与服务器通信的时候,如果出现错误,请求会被传给下一个服务器,直到所有可用的服务器都被尝试过。 如果所有服务器都返回失败,客户端将会得到最后通信的那个服务器的(失败)响应结果。

语法:server address [parameters];
默认值:
上下文:upstream

定义服务器的地址address和其他参数parameters。 地址可以是域名或者IP地址,端口是可选的,或者是指定“unix:”前缀的UNIX域套接字的路径。如果没有指定端口,就使用80端口。 如果一个域名解析到多个IP,本质上是定义了多个server。

你可以定义下面的参数:weight=number设定服务器的权重,默认是1。max_fails=number设定Nginx与服务器通信的尝试失败的次数。在fail_timeout参数定义的时间段内,如果失败的次数达到此值,Nginx就认为服务器不可用。在下一个fail_timeout时间段,服务器不会再被尝试。 失败的尝试次数默认是1。设为0就会停止统计尝试次数,认为服务器是一直可用的。 你可以通过指令proxy_next_upstream、 fastcgi_next_upstream和memcached_next_upstream来配置什么是失败的尝试。 默认配置时,http_404状态不被认为是失败的尝试。fail_timeout=time设定

  • 统计失败尝试次数的时间段。在这段时间中,服务器失败次数达到指定的尝试次数,服务器就被认为不可用。
  • 服务器被认为不可用的时间段。

默认情况下,该超时时间是10秒。backup标记为备用服务器。当主服务器不可用以后,请求会被传给这些服务器。down标记服务器永久不可用,可以跟ip_hash指令一起使用。

Example:

upstream backend {
    server backend1.example.com     weight=5;
    server 127.0.0.1:8080           max_fails=3 fail_timeout=30s;
    server unix:/tmp/backend3;

    server backup1.example.com:8080 backup;
}
语法:ip_hash;
默认值:
上下文:upstream

指定服务器组的负载均衡方法,请求基于客户端的IP地址在服务器间进行分发。 IPv4地址的前三个字节或者IPv6的整个地址,会被用来作为一个散列key。 这种方法可以确保从同一个客户端过来的请求,会被传给同一台服务器。除了当服务器被认为不可用的时候,这些客户端的请求会被传给其他服务器,而且很有可能也是同一台服务器。

从1.3.2和1.2.2版本开始支持IPv6地址。

如果其中一个服务器想暂时移除,应该加上down参数。这样可以保留当前客户端IP地址散列分布。

例子:

upstream backend {
    ip_hash;

    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com down;
    server backend4.example.com;
}

从1.3.1和1.2.2版本开始,ip_hash的负载均衡方法才支持设置服务器权重值。

语法:keepalive connections;
默认值:
上下文:upstream

这个指令出现在版本 1.1.4.

激活对上游服务器的连接进行缓存。

connections参数设置每个worker进程与后端服务器保持连接的最大数量。这些保持的连接会被放入缓存。 如果连接数大于这个值时,最久未使用的连接会被关闭。

需要注意的是,keepalive指令不会限制Nginx进程与上游服务器的连接总数。 新的连接总会按需被创建。 connections参数应该稍微设低一点,以便上游服务器也能处理额外新进来的连接。

配置memcached上游服务器连接keepalive的例子:

upstream memcached_backend {
    server 127.0.0.1:11211;
    server 10.0.0.2:11211;

    keepalive 32;
}

server {
    ...

    location /memcached/ {
        set $memcached_key $uri;
        memcached_pass memcached_backend;
    }

}

对于HTTP代理,proxy_http_version指令应该设置为“1.1”,同时“Connection”头的值也应被清空。

upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
}

server {
    ...

    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        ...
    }
}

另外一种选择是,HTTP/1.0协议的持久连接也可以通过发送“Connection: Keep-Alive”头来实现。不过不建议这样用。

对于FastCGI的服务器,需要设置 fastcgi_keep_conn 指令来让连接keepalive工作:

upstream fastcgi_backend {
    server 127.0.0.1:9000;

    keepalive 8;
}

server {
    ...

    location /fastcgi/ {
        fastcgi_pass fastcgi_backend;
        fastcgi_keep_conn on;
        ...
    }
}

当使用的负载均衡方法不是默认的轮转法时,必须在keepalive 指令之前配置。

针对SCGI和uwsgi协议,还没有实现其keepalive连接的打算。

语法:least_conn;
默认值:
上下文:upstream

这个指令出现在版本 1.3.1 和 1.2.2.

指定服务器组的负载均衡方法,根据其权重值,将请求发送到活跃连接数最少的那台服务器。 如果这样的服务器有多台,那就采取有权重的轮转法进行尝试。

嵌入的变量

ngx_http_upstream_module模块支持以下嵌入变量:

$upstream_addr保存服务器的IP地址和端口或者是UNIX域套接字的路径。 在请求处理过程中,如果有多台服务器被尝试了,它们的地址会被拼接起来,以逗号隔开,比如: “192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock”。 如果在服务器之间通过“X-Accel-Redirect”头或者error_page有内部跳转,那么这些服务器组之间会以冒号隔开,比如:“192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80”。$upstream_response_time以毫秒的精度保留服务器的响应时间,(输出)单位是秒。 出现多个响应时,也是以逗号和冒号隔开。$upstream_status保存服务器的响应代码。 出现多个响应时,也是以逗号和冒号隔开。$upstream_http_...保存服务器的响应头的值。比如“Server”响应头的值可以通过$upstream_http_server变量来获取。 需要注意的是只有最后一个响应的头会被保留下来。

分类: nginx 标签:

nginx rtmp流媒体直播服务器配置

2019年7月9日 没有评论

nginx是一个轻量级的web服务器,通过RTMP模块可以提供流媒体服务。RTMP没有预编译好的包,需要从源码编译。

安装nginx和RTMP模块

本文在ubuntu环境实现。安装前的编译工具准备:

$ sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev

下载nginx源码包:

$ wget http://nginx.org/download/nginx-1.15.1.tar.gz

从git上下载RTMP模块源码:

$ wget https://github.com/sergey-dryabzhinsky/nginx-rtmp-module/archive/dev.zip

解压两个压缩包,进入nginx文件夹:

$ tar -zxvf nginx-1.15.1.tar.gz
$ unzip dev.zip
$ cd nginx-1.15.1

编译带有rtmp模块的nginx:

$ ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-dev
$ make
$ sudo make install

到此,nginx安装完成。默认安装到 /usr/local/nginx, 启动命令

$ sudo /usr/local/nginx/sbin/nginx

测试nginx是否正常工作,使用浏览器打开http://ip/,可以看到 "Welcome to nginx!" 页面。

nginx配置RTMP模块

打开配置文件,位置在/usr/local/nginx/conf/nginx.conf ,添加如下配置:

rtmp {
        server {
                listen 1935;
                chunk_size 4096;

                application live {
                        live on;
                        record off;
                }
        }
}

这个一个最基础的直播流配置,把RTMP流发送给请求者。

重启nginx:

$ sudo /usr/local/nginx/sbin/nginx -s stop
$ sudo /usr/local/nginx/sbin/nginx

测试

1.配置OBS推流

新建一个场景,配置如下:

Streaming Service: Custom
Server: rtmp://<your server ip>/live
Play Path/Stream Key: test

2.播放流

使用VLC v2.1.0以后版本,打开网络流文件,输入rtmp://<your server ip>/live/test 就可以看到视频了!

rtmp完整配置,

分类: nginx 标签:

理解Nginx的server匹配规则

2019年6月27日 没有评论

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模块中。

1
2
3
4
server{
listen 192.168.1.10;
....
}
1
2
3
4
5
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模块将被选中:

1
2
3
4
5
server{
listen 80;
server_name *.example.com;
...
}
1
2
3
4
5
server{
listen 80;
server_name host1.example.com;
...
}

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

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

1
2
3
4
5
server{
listen 80;
server_name www.example.*;
...
}
1
2
3
4
5
server{
listen 80;
server_name *.example.org;
...
}
1
2
3
4
server{
listen 80;
server_name *.org;
}

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

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

1
2
3
4
5
server{
listen 80;
server_name host1.example.com;
...
}
1
2
3
4
server{
listen 80;
server_name example.com;
}
1
2
3
4
server{
listen 80;
server_name www.example.*;
}

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

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

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

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

分类: nginx 标签: