nginx配置之server_name

介绍nginx中server_name的用法。

服务器名称通过server_name指令定义,用于决定请求由哪个server块进行处理。可以用具体的名称、通配符名称或正则表达式定义。

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  ~^(?.+)\.example\.net$;
    ...
}

当通过名称查询一个虚拟主机时,如果名称匹配了超过一个的变量,例如通配符和正则都匹配,则先匹配上的变量会被选中,优先级顺序如下:

  1. 具体的名称
  2. 以*开头的最长的通配符
  3. 以*结尾的通配符
  4. 第一个匹配的正则表达式(按出现在配置文件中的先后顺序)

通配符
通配符的*只能出现在名称的开头或结尾,并且只能使用英文句号“.”作为边界。名称“www.*.example.org”或“w*.example.org”是无效的。但这些名称可以通过正则表达式定义,例如“~^www\..+\.example\.org$”及“~^w.*\.example\.org$”。一个*可以匹配多个名字部分。名字“*.example.org”不仅匹配www.example.org还匹配www.sub.example.org
正则表达式
nginx使用的Perl正则表达式语法(PCRE),要用正则表达式,必须以~开头:
server_name ~^www\d+\.example\.net$;
否则会被认为是一个确切的名称,如果存在*,还被认为是统配符(这往往是不对的)。不要忘记添加“^”和“$”,语法上不要求加,但加上更符合逻辑。另外,注意域名的点应该用“\”进行转义。含有“{”和“}”的正则表达式需要用引号包裹:
server_name "~^(?\w\d{1,3}+)\.example\.net$";
否则,nginx启动会报错:
directive "server_name" is not terminated by ";" in ...
名称的正则表达式的捕获信息可以用作变量:

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

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

PCRE库支持的用法如下:

? Perl 5.10 compatible syntax, supported since PCRE-7.0
?’name’ Perl 5.10 compatible syntax, supported since PCRE-7.0
?P Python compatible syntax, supported since PCRE-4.0

如果nginx启动的时候报如下错误:
pcre_compile() failed: unrecognized character after (?< in ...
说明PCRE库比较老,试试改成?P语法。捕获信息也可以用数字代替:

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

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

但是,这种用法仅限使用在比较简单的案例中,因为用数字代替很容易被覆盖重写。
混合名称
有一些特殊处理的服务器名称。
如果需要处理http请求没有Host域,并且没有设置默认服务器,需要定义一个空的名称:

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

如果server块中没有定义服务器名称,nginx默认使用空字符串作为名称。

nginx直到0.8.48版本,使用的是机器的主机名作为服务器名称

如果服务器名称定义为“$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指令定义全捕获的名称或默认名称。这是listen指令的属性而不是server_name的指令。可以定义不同的服务器分别监听*:80和*:8080,其中一个是80端口的默认服务器,另一个是8080端口的默认服务器:

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

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

优化
具体名称、*开头的通配符名称、*结尾的通配符名称分配存储在三张哈希表中并绑定不同的端口。在配置阶段会对哈希表的大小进行优化,一般提高名称在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;
    ...
}

如果定义了大量的名称,或一个名称很长,需要用到server_names_hash_max_size和server_names_hash_bucket_size指令,它们应该写在http块内。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的值。
如果只配置了一个server块监听端口,nginx根本不会检测服务器名称(也不会建立哈希表)。但有一个例外,如果名称是一个有捕获的正则表达式,nginx会执行正则表达式以便获取捕获信息。
兼容性

  • 特殊的服务器名称“$hostname”从0.9.4开始支持。
  • 从0.8.48开始默认的服务器名称是空的。
  • Named regular expression server name captures have been supported since 0.8.25.
  • Regular expression server name captures have been supported since 0.7.40.
  • 空的服务器名称从0.7.12开始被支持
  • 通配符和正则表达式从0.6.25版本开始被支持
  • 正则表达式服务器名称从0.6.7版本被支持
  • 通配符example.*形式从0.6.0版本开始被支持
  • 特殊形式.example.org从0.3.18版本开始被支持
  • 统配符*.example.org形式从0.1.13版本开始被支持