了解服务器名称

服务器名称使用 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$;
    ...
}

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

(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  ~^wwwd+.example.net$;

否则,它将被视为确切名称,或者如果表达式包含星号,则将其视为通配符名称(并且很可能被视为无效名称)。不要忘记设置“^”和“$”锚点。 它们在语法上不是必需的,但在逻辑上是必需的。 另请注意,域名点应使用反斜杠进行转义。应引用包含字符“{”和“}”的正则表达式:

server_name  "~^(?<name>wd{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<name>”。捕获也可以以数字形式使用:

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

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

但是,这种用法应仅限于简单的情况(如上述),因为数字参考很容易被覆盖。

各种各样的名字

有一些服务器名称被特殊对待。

如果需要在非默认服务器块中处理没有“Host”标头字段的请求,则应指定一个空名称:

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

如果 server_name 未在 server 块中定义,则 nginx 使用空名称作为服务器名称。

注意:在这种情况下,直到 0.8.48 的 nginx 版本使用机器的主机名作为服务器名称。

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

如果有人使用 IP 地址而不是服务器名称发出请求,“Host”请求头字段将包含 IP 地址,并且可以使用 IP 地址作为服务器名称来处理请求:

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

在包罗万象的服务器示例中,可以看到奇怪的名称“_”:

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

这个名字没有什么特别之处,它只是无数从不与任何真实名称相交的无效域名之一。 同样可以使用其他无效名称,如“--”和“!@#”。

直到 0.6.25 的 nginx 版本支持特殊名称“*”,它被错误地解释为一个包罗万象的名称。它从来没有作为一个包罗万象的或通配符的服务器名称。相反,它提供了现在由 server_name_in_redirect 指令提供的功能。特殊名称“*”现已弃用,应使用 server_name_in_redirect 指令。请注意,无法使用 server_name 指令指定全能名称或默认服务器。这是 listen 指令的属性,而不是 server_name 指令的属性。另请参阅“nginx 如何处理请求?”。 可以定义侦听端口 *:80 和 *:8080 的服务器,并指示其中一个将是端口 *:8080 的默认服务器,而另一个将是端口 *:80 的默认服务器:

server {
    listen       80;
    listen       8080  default_server;
    server_name  example.net;
    ...
}

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

国际化名称

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

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

选择虚拟服务器

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

  • 根据 SNI,在 SSL 握手期间,要提前

  • 处理请求行后

  • 处理 Host 头字段后

  • 如果在处理请求行或 Host 标头字段后未确定服务器名称,则 nginx 将使用空名称作为服务器名称。

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

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

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

  • 对于处理请求头域涉及的ignore_invalid_headers、large_client_header_buffers和underscores_in_headers指令,还取决于服务器配置是根据请求行还是Host头域更新的;

  • 错误响应将在当前满足请求的服务器中使用 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 指令。server_names_hash_bucket_size 指令的默认值可能等于 32、64 或其他值,具体取决于 CPU 缓存行大小。如果默认值为 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: 512
or 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 开始支持正则表达式服务器名称

  • 从 0.6.0 开始支持通配符形式 example.*

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

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

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号