VirtualHost Examples
本文档尝试回答有关设置virtual hosts的常见问题。这些方案涉及通过name-based或IP-based虚拟主机在单个服务器上运行的多个网站。
在一个 IP 地址上运行多个基于名称的网站。
您的服务器具有多个解析为单个地址的主机名,并且您希望对www.example.com
和www.example.org
做出不同的响应。
Note
在 Apache 服务器上创建虚拟主机配置不会神奇地导致为这些主机名创建 DNS 条目。您必须在 DNS 中拥有名称,解析为您的 IP 地址,否则其他人将无法看到您的网站。您可以将条目放在hosts
文件中以进行本地测试,但这仅在具有这些hosts
条目的计算机上有效。
# Ensure that Apache listens on port 80
Listen 80
<VirtualHost *:80>
DocumentRoot "/www/example1"
ServerName www.example.com
# Other directives here
</VirtualHost>
<VirtualHost *:80>
DocumentRoot "/www/example2"
ServerName www.example.org
# Other directives here
</VirtualHost>
星号匹配所有地址,因此主服务器不处理任何请求。由于带有ServerName www.example.com
的虚拟主机位于配置文件中的第一位,因此它具有最高优先级,可以被视为默认服务器或主服务器。这意味着,如果接收到与指定的ServerName指令不匹配的请求,则此第一个<VirtualHost>将为该请求提供服务。
上面的配置是您几乎希望在所有基于名称的虚拟主机情况下使用的配置。实际上,此配置唯一不起作用的是基于不同的 IP 地址或端口提供不同的内容时。
Note
您可以将*
替换为系统上的特定 IP 地址。此类虚拟主机仅用于连接到指定 IP 地址时收到的 HTTP 请求。
但是,在 IP 地址不可预测的系统上使用*
很有用,例如,如果您的 ISP 具有动态 IP 地址,并且您正在使用各种动态 DNS 解决方案。由于*
与任何 IP 地址都匹配,因此只要您的 IP 地址发生更改,此配置就不会更改。
多个 IP 地址上的基于名称的主机。
Note
此处讨论的任何技术都可以扩展到任意数量的 IP 地址。
该服务器有两个 IP 地址。在一个(172.20.30.40
)上,我们将为“主”服务器server.example.com
提供服务,在另一个(172.20.30.50
)中,我们将为两个或多个虚拟主机提供服务。
Listen 80
# This is the "main" server running on 172.20.30.40
ServerName server.example.com
DocumentRoot "/www/mainserver"
<VirtualHost 172.20.30.50>
DocumentRoot "/www/example1"
ServerName www.example.com
# Other directives here ...
</VirtualHost>
<VirtualHost 172.20.30.50>
DocumentRoot "/www/example2"
ServerName www.example.org
# Other directives here ...
</VirtualHost>
主服务器将向172.20.30.50
以外的地址发出任何请求。主机名未知或没有Host:
Headers 的172.20.30.50
请求将从www.example.com
提供。
在不同的 IP 地址(例如内部和外部地址)上提供相同的内容。
服务器计算机具有两个 IP 地址(192.168.1.1
和172.20.30.40
)。机器位于内部(内部网)网络和外部(内部网)网络之间。在网络外部,名称server.example.com
解析为外部地址(172.20.30.40
),但是在网络内部,同一名称解析为内部地址(192.168.1.1
)。
只需一个<VirtualHost>部分,就可以使服务器以相同的内容响应内部和外部请求。
<VirtualHost 192.168.1.1 172.20.30.40>
DocumentRoot "/www/server1"
ServerName server.example.com
ServerAlias server
</VirtualHost>
现在,来自两个网络的请求将通过相同的<VirtualHost>进行服务。
Note:
在内部网络上,只能使用名称server
而不是标准主机名server.example.com
。
还要注意,在上面的示例中,您可以将 IP 地址列表替换为*
,这将导致服务器在所有地址上做出相同的响应。
在不同的端口上运行不同的站点。
您有多个要使用相同 IP 的域,并且还想为多个端口提供服务。下面的示例说明了名称匹配是在确定最佳匹配的 IP 地址和端口组合之后进行的。
Listen 80
Listen 8080
<VirtualHost 172.20.30.40:80>
ServerName www.example.com
DocumentRoot "/www/domain-80"
</VirtualHost>
<VirtualHost 172.20.30.40:8080>
ServerName www.example.com
DocumentRoot "/www/domain-8080"
</VirtualHost>
<VirtualHost 172.20.30.40:80>
ServerName www.example.org
DocumentRoot "/www/otherdomain-80"
</VirtualHost>
<VirtualHost 172.20.30.40:8080>
ServerName www.example.org
DocumentRoot "/www/otherdomain-8080"
</VirtualHost>
基于 IP 的虚拟主机
该服务器有两个 IP 地址(172.20.30.40
和172.20.30.50
),分别解析为名称www.example.com
和www.example.org
。
Listen 80
<VirtualHost 172.20.30.40>
DocumentRoot "/www/example1"
ServerName www.example.com
</VirtualHost>
<VirtualHost 172.20.30.50>
DocumentRoot "/www/example2"
ServerName www.example.org
</VirtualHost>
如果有<VirtualHost>
指令之一(例如localhost
)中未指定的任何地址的请求,将转到主服务器。
基于端口和基于 IP 的混合虚拟主机
服务器计算机具有两个 IP 地址(172.20.30.40
和172.20.30.50
),分别解析为名称www.example.com
和www.example.org
。在每种情况下,我们都希望在端口 80 和 8080 上运行主机。
Listen 172.20.30.40:80
Listen 172.20.30.40:8080
Listen 172.20.30.50:80
Listen 172.20.30.50:8080
<VirtualHost 172.20.30.40:80>
DocumentRoot "/www/example1-80"
ServerName www.example.com
</VirtualHost>
<VirtualHost 172.20.30.40:8080>
DocumentRoot "/www/example1-8080"
ServerName www.example.com
</VirtualHost>
<VirtualHost 172.20.30.50:80>
DocumentRoot "/www/example2-80"
ServerName www.example.org
</VirtualHost>
<VirtualHost 172.20.30.50:8080>
DocumentRoot "/www/example2-8080"
ServerName www.example.org
</VirtualHost>
基于名称和基于 IP 的混合虚拟主机
在虚拟主机的参数中提到的任何地址都永远不会出现在其他虚拟主机中,这是严格基于 IP 的虚拟主机。
Listen 80
<VirtualHost 172.20.30.40>
DocumentRoot "/www/example1"
ServerName www.example.com
</VirtualHost>
<VirtualHost 172.20.30.40>
DocumentRoot "/www/example2"
ServerName www.example.org
</VirtualHost>
<VirtualHost 172.20.30.40>
DocumentRoot "/www/example3"
ServerName www.example.net
</VirtualHost>
# IP-based
<VirtualHost 172.20.30.50>
DocumentRoot "/www/example4"
ServerName www.example.edu
</VirtualHost>
<VirtualHost 172.20.30.60>
DocumentRoot "/www/example5"
ServerName www.example.gov
</VirtualHost>
一起使用 Virtual_host 和 mod_proxy
以下示例允许前端计算机将虚拟主机代理到另一台计算机上运行的服务器。在此示例中,在192.168.111.2
的计算机上配置了具有相同名称的虚拟主机。使用ProxyPreserveHost On指令,以便在我们将多个主机名代理到一台计算机的情况下,传递所需的主机名。
<VirtualHost *:*>
ProxyPreserveHost On
ProxyPass "/" "http://192.168.111.2/"
ProxyPassReverse "/" "http://192.168.111.2/"
ServerName hostname.example.com
</VirtualHost>
使用_default 虚拟主机
所有端口 _default 虚拟主机
捕获“每一个”请求到任何未指定的 IP 地址和端口即,该地址/端口组合未用于任何其他虚拟主机。
<VirtualHost _default_:*>
DocumentRoot "/www/default"
</VirtualHost>
将这样的默认虚拟主机与通配符端口一起使用可有效防止任何请求发送到主服务器。
默认虚拟主机从不处理已发送到用于基于名称的虚拟主机的地址/端口的请求。如果请求中包含未知或没有Host:
Headers,则始终从基于主名称的虚拟主机(该地址/端口的虚拟主机首先出现在配置文件中)提供服务。
您可以使用AliasMatch或RewriteRule将任何请求重写到单个信息页(或脚本)。
_default 虚拟主机用于不同的端口
与设置 1 相同,但是服务器侦听多个端口,并且我们想为端口 80 使用第二个_default_
虚拟主机。
<VirtualHost _default_:80>
DocumentRoot "/www/default80"
# ...
</VirtualHost>
<VirtualHost _default_:*>
DocumentRoot "/www/default"
# ...
</VirtualHost>
端口 80 的默认虚拟主机(“ *”必须出现在任何带通配符端口的默认虚拟主机之前)捕获所有发送到未指定 IP 地址的请求。主服务器从不用于服务请求。
一个端口 _default 虚拟主机
我们希望为端口 80 提供一个默认虚拟主机,但没有其他默认虚拟主机。
<VirtualHost _default_:80>
DocumentRoot "/www/default"
...
</VirtualHost>
默认虚拟主机会向端口 80 上的未指定地址发出请求。主服务器会处理对未指定地址和端口的任何其他请求。
在虚拟主机声明中使用*
的优先级将高于_default_
。
将基于名称的虚拟主机迁移到基于 IP 的虚拟主机
主机名为www.example.org
的基于名称的虚拟主机(以我们的name-based示例,设置 2 为例)应获得其自己的 IP 地址。为避免名称服务器或为基于名称的虚拟主机缓存旧 IP 地址的代理出现问题,我们希望在迁移阶段提供两种变体。
解决方案很简单,因为我们可以简单地将新 IP 地址(172.20.30.50
)添加到VirtualHost
指令中。
Listen 80
ServerName www.example.com
DocumentRoot "/www/example1"
<VirtualHost 172.20.30.40 172.20.30.50>
DocumentRoot "/www/example2"
ServerName www.example.org
# ...
</VirtualHost>
<VirtualHost 172.20.30.40>
DocumentRoot "/www/example3"
ServerName www.example.net
ServerAlias *.example.net
# ...
</VirtualHost>
现在,可以通过新地址(作为基于 IP 的虚拟主机)和旧地址(作为基于名称的虚拟主机)访问该虚拟主机。
使用 ServerPath 指令
我们有一台带有两个基于名称的虚拟主机的服务器。为了匹配正确的虚拟主机,Client 端必须发送正确的Host:
Headers。旧的 HTTP/1.0 Client 端不会发送这样的 Headers,而 Apache 不知道 Client 端试图到达哪个虚拟主机(并服务于来自主要虚拟主机的请求)。为了提供尽可能多的向后兼容性,我们创建了一个主虚拟主机,该主机返回一个页面,该页面包含指向基于名称的虚拟主机的 URL 前缀的链接。
<VirtualHost 172.20.30.40>
# primary vhost
DocumentRoot "/www/subdomain"
RewriteEngine On
RewriteRule "." "/www/subdomain/index.html"
# ...
</VirtualHost>
<VirtualHost 172.20.30.40>
DocumentRoot "/www/subdomain/sub1"
ServerName www.sub1.domain.tld
ServerPath "/sub1/"
RewriteEngine On
RewriteRule "^(/sub1/.*)" "/www/subdomain$1"
# ...
</VirtualHost>
<VirtualHost 172.20.30.40>
DocumentRoot "/www/subdomain/sub2"
ServerName www.sub2.domain.tld
ServerPath "/sub2/"
RewriteEngine On
RewriteRule "^(/sub2/.*)" "/www/subdomain$1"
# ...
</VirtualHost>
由于ServerPath指令,始终从 sub1-vhost 提供对 URL http://www.sub1.domain.tld/sub1/
的请求。
如果 Client 端发送了正确的Host:
Headers,则仅从 sub1-vhost 提供对 URL http://www.sub1.domain.tld/
的请求。如果未发送Host:
Headers,则 Client 端将从主主机获取信息页。
请注意,有一个奇怪的地方:如果 Client 端未发送Host:
Headers,则 sub1-vhost 也会向http://www.sub2.domain.tld/sub1/
提供请求。
RewriteRule指令用于确保发送正确Host:
Headers 的 Client 端可以使用带有或不带有 URL 前缀的两个 URL 变体* i.e. *。