vlambda博客
学习文章列表

nginx虚拟服务名称详解,不信你有我全

-《软件测试干货文TOP3》第211期 -


叮~柠檬班新栏目栏目「干货文TOP3」啦!


在 「干货文TOP3」
一期一个知识点
5分钟解答你最关心的软件测试问题
简单明了,清晰易学的干货
你关心的一切软件测试问题,都在这里啦!
nginx虚拟服务名称详解,不信你有我全

服务器名是使用 server_name 指令定义的,并确定哪个 server 块用于给定的请求。参见“nginx 如何处理请求”。

它们可以使用精确名称、通配符名称或正则表达式来定义:

server { listen 80; server_name example.org www.example.org; ...}
server { listen 80; server_name *.example.org; ...}
server { listen 80; server_name mail.*; ...}
server { listen 80; server_name ~^(?<user>.+)\.example\.net$; ...}

当按名称搜索虚拟服务器时,如果名称匹配多个指定的名称,例如,通配符名称和正则表达式名称都匹配,第一个匹配的名称将被选择,优先顺序如下:

  1. 精确的名称

  2. * 开头的最长的通配符名称,例如 *.example.org

  3. * 结尾的最长的通配符名称,例如 mail.*

  4. 第一个匹配的正则表达式名称(按配置文件中出现的顺序排列)


►►►

通配符名称


通配符名称只能在名称的开始或结束处包含星号,并且只能在点符号 . 的旁边。名称 www.*.example.org 和 w*.example.org 都是无效的。

但是,可以使用正则表达式指定这些名称,例如:~^www\..+\.example\.org$ 和 ~^w.*\.example\.org$。星号可以匹配多个名称部分。

*.example.org 不仅可以匹配 www.example.org 也可以匹配www.sub.example.org。

.example.org 是一个特殊的通配符名称,它可以匹配确切的名称 example.org 和通配符名称 *.example.org。


►►►

正则表达式名称


nginx 使用的正则表达式与 Perl 编程语言(PCRE)使用的正则表达式是兼容的。要使用正则表达式,服务器名必须以波浪字符开头:

server_name ~^www\d+\.example\.net$;
否则,它将被视为精确名称,或者如果表达式包含星号,则被视为通配符名称(而且很可能是无效名称)。
不要忘记设置" ^ "和" $ "。它们在语法上不是必需的,但在逻辑上却是必需的。还要注意,域名点应该用反斜杠进行转义。包含字符"{"和"}"的正则表达式应该用引号括起来:
server_name "~^(?<name>\w\d{1,3}+)\.example\.net$";

否则 nginx 将无法启动,并显示错误信息:

directive "server_name" is not terminated by ";" in ...

命名正则表达式可以捕获作为一个变量使用:

server { server_name ~^(www\.)?(?<domain>.+)$;
location / { root /sites/$domain; }}

PCRE 库使用以下语法支持命名捕获:

  • ?<name>
Perl 5.10兼容的语法,从PCRE-7.0开始支持
  • ?'name'
Perl 5.10兼容的语法,从PCRE-7.0开始支持
  • ?p<name>
Python兼容语法,从PCRE-4.0开始支持

如果 nginx 启动失败,并显示如下错误提示:

pcre_compile() failed: unrecognized character after (?< in ...

这意味着 PCRE 库是旧的,应该尝试改为语法“?P "。分组捕获也可以以数字形式使用:

server { server_name ~^(www\.)?(.+)$;

location / { root /sites/$2; }}

然而,这种用法应该限于简单的情况(如上述),因为数字引用很容易被覆盖。


►►►

其他名称


有一些服务名称被特殊对待。如果需要处理服务器块中没有" Host "报头字段的请求,这不是默认值,应该指定一个空的名称:

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

如果服务器块中没有定义 server_name,那么 nginx 使用空名称作为服务器名称。

  0.8.48 之前的 Nginx 版本使用机器的主机名作为服务器名

如果服务器名定义为“$hostname”(0.9.4),则使用该机器的主机名。

server { listen 80; server_name example.org www.example.org "" 192.168.1.1 ; ...}

在 catch-all 的例子中我们可以看到特殊名称 _

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

这个名字没什么特别的,它只是无数无效域名中的一个,永远不会与任何真实名称相交。其他无效的名称如 __!@# 也同样适用。

Nginx 版本到 0.6.25 都支持特殊名称 *,它被错误地解释为一个 catch-all 名称。它从未作为 catch-all 或通配符服务器名。相反,它提供了现在由 server_name_in_redirect 指令提供的功能。特殊名称 * 现在已弃用,应该使用 server_name_in_redirect 指令。

►►►

名称国际化

国际化域名(idn)应该在 server_name 指令中用 ASCII (Punycode)表示来指定:

server { listen 80; server_name xn--e1afmkfd.xn--80akhbyknj4f; # пример.испытание ...}


►►►

虚拟服务器的选择


首先,在默认的服务器上下文中创建连接。然后,可以在以下请求处理阶段确定服务器名,每个阶段都涉及服务器配置选择:

  • 在 SSL 握手时,提前,根据 SNI

  • 在处理请求行之后

  • 在处理 Host 报头字段之后

  • 如果在处理请求行或者 Host 报头字段后没有确定服务器名,nginx 将使用空的名称作为服务器名。

在每个阶段,可以应用不同的服务器配置。因此,某些指令应谨慎指定:

  • 在 ssl_protocols 指令的情况下,协议列表由 OpenSSL 库设置,然后根据 SNI 请求的名称应用服务器配置,因此,协议应该仅为默认服务器指定;

  • 在读取请求行之前,涉及到 client_header_buffer_size 和 merge_slashes 指令,因此,此类指令使用默认的服务器配置或 SNI 选择的服务器配置;

  • 在处理请求报头字段时,涉及到 ignore_invalid_headers、large_client_header_buffers 和 underscores_in_headers 指令,它还取决于服务器配置是否根据请求行或主机报头字段更新;

  • 在当前执行请求的服务器中,error_page 指令将处理错误响应。


►►►

优化


确切的名称、以星号开头的通配符名称和以星号结尾的通配符名称存储在绑定到侦听端口的三个散列表中。哈希表的大小在配置阶段进行了优化,以便在 CPU 缓存缺失最少的情况下找到名称。建立哈希表的详细信息在一个单独的文档中提供。

首先搜索确切的名字哈希表。如果没有找到名称,则搜索通配符名称以星号开头的散列表。如果在那里没有找到名称,则搜索以星号结尾的通配符名称的散列表。

搜索通配符名称哈希表比搜索精确名称哈希表慢,因为名称是按域名部分搜索的。注意,特殊的通配符形式“.example.org”存储在通配符名称哈希表中,而不是精确名称哈希表中。

正则表达式是按顺序测试的,因此是最慢的方法,而且是不可伸缩的。

由于这些原因,在可能的情况下最好使用确切的名称。例如,如果服务器中被请求次数最多的名称是 example.org 和 www.example.org,那么显式定义它们会更有效率:

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

而不是使用简化形式:

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

如果定义了大量的服务器名,或者定义了非常长的服务器名,可能需要在 HTTP 级别调优 server_names_hash_max_size 和server_names_hash_bucket_size 指令。

根据 CPU 缓存行大小的不同,server_names_hash_bucket_size 指令的默认值可以是 32、64 或其他值。

如果默认值是 32,并且服务器名被定义为“too.long.server.name.example.org”,那么 nginx 将启动失败,并显示错误信息:

could not build the server_names_hash,you should increase server_names_hash_bucket_size: 32

在这种情况下,指令值应该增加到 2 的下一次方:

 
   
   
 
http { server_names_hash_bucket_size 64; ...

如果定义了大量的服务器名,则会出现另一个错误消息:

could not build the server_names_hash,you should increase either server_names_hash_max_size: 512or server_names_hash_bucket_size: 32

在这种情况下,首先尝试将 server_names_hash_max_size 设置为接近服务器名称数量的数字。只有当这没有帮助,或者如果 nginx 的开始时间长得无法接受,尝试增加 server_names_hash_bucket_size。

如果服务器是监听端口的唯一服务器,那么 nginx 根本不会测试服务器名称(并且不会为侦听端口构建哈希表)。然而,有一个例外。

如果服务器名是带有捕获的正则表达式,那么 nginx 必须执行该表达式来获取捕获。


►►►

兼容性


  • 从 0.9.4 开始就支持特殊的服务器名称“$hostname”。

  • 从 0.8.48 开始,默认的服务器名值为空名称“”。

  • 自 0.8.25 以来,已支持命名正则表达式服务器名称捕获。

  • 正则表达式服务器名称捕获从 0.7.40 开始支持。

  • 自 0.7.12 以来,支持一个空的服务器名" "。

  • 自 0.6.25 以来,支持使用通配符服务器名或正则表达式作为第一个服务器名。

  • 正则表达式服务器名从 0.6.7 开始被支持。

  • example.* 这样的通配符形式从 0.6.0 开始支持。

  • 从 0.3.18 开始就支持特殊形式 .example.org

  • 通配符形式 *.example.org 从 0.1.13 开始支持。

编辑:Brian Mercer

翻译:心蓝

nginx虚拟服务名称详解,不信你有我全

今天的小知识学会了么
欢迎在留言区跟我们互动噢~