我想建立一个可以动态创建 Apache 虚拟主机而无需重新加载配置的环境。
我可以这样做mod_vhost_alias,我设置了我的默认虚拟主机如下
<VirtualHost *>
UseCanonicalName Off
VirtualDocumentRoot /var/www/sandboxes/domains/%0
ServerName catchall.host
</VirtualHost>
这工作得很好,但如果请求当前未设置的主机名,我会收到 404 Not Found 错误。
我想真的喜欢做的只是让这个VirtualHost启动仅有的如果文档根目录存在,否则让它尝试匹配另一个虚拟主机(换句话说,使虚拟文档根与使用 ServerAlias 的方式相同)
我尝试将其设为第二个虚拟主机,第一个虚拟主机只处理所有请求,但这不起作用 - 对于配置了 VirtualDocumentRoot 的域的请求都落到了默认虚拟主机。
那么,我怎样才能动态配置虚拟主机,但对于尚未配置的虚拟主机,又可以回退到另一个虚拟主机呢?
答案1
我找到了一种适合我的解决方法。
我可以使用錯誤文檔来获取 PHP 脚本中的 404 错误。起初,这似乎有问题。如果没有 DocumentRoot,脚本将驻留在何处?
我可以使用来自不同域名的 URL 来显示错误消息。然而,在测试中我发现无法知道最初请求的域名是什么。
答案是别名一个目录并从中提供错误,所以我的虚拟主机看起来像这样
<VirtualHost *>
UseCanonicalName Off
VirtualDocumentRoot /var/www/sandboxes/domains/%0
ServerName catchall.host
Alias /errors /var/www/default/errors/
ErrorDocument 404 /errors/notfound.php
</VirtualHost>
现在,当请求未配置的域时,将改为调用 /var/www/default/errors/notfound.php 处的脚本。此脚本可以检查 $_SERVER['HTTP_HOST'] 以查看请求了哪个域。如果实际已配置,则会出现常规 404。如果未配置,则可以显示一些替代错误消息。就我而言,这是我显示 UI 以帮助设置 vhost 的地方。
答案2
我在谷歌搜索时找到了你的问题。我遇到了完全相同的问题,并按照描述进行了修复保罗的回答。但是,对于我的复杂 Web 应用程序,我并不乐意将所有请求都通过一个单一的 进行路由notfound.php
。
最后,我设法在没有外部脚本的情况下解决了该问题,只需编辑我的 VirtualHost 配置即可。
最初,我的VirtualHost配置如下:
<VirtualHost *:80>
Usecanonicalname Off
Virtualdocumentroot /mnt/ramdisk/www/cms-%-3.0-development/
</VirtualHost>
我有类似这样的 URL client1.domainname.com
,client2.domainname.com
以及whatever.domainname.com
决议/mnt/ramdisk/www/cms-client1-development/
,/mnt/ramdisk/www/cms-client2-development/
并/mnt/ramdisk/www/cms-whatever-development/
但是,像这样的 URLnonexistent.domainname.com
会给我一个 404,因为目录/mnt/ramdisk/www/cms-nonexistent-development/
不存在。我希望这些子域名使用该目录/mnt/ramdisk/www/cms-default-development/
我通过使用ModRewrite
和解决了这个问题ModProxy
:
<VirtualHost *:80>
Usecanonicalname Off
Virtualdocumentroot /mnt/ramdisk/www/cms-%-3.0-development/
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.domainname\.com$ [NC]
RewriteCond /mnt/ramdisk/www/cms-%1-development/ !-d
RewriteRule (.*) http://default.domainname.com/$1 [P,L]
</VirtualHost>
其作用是:获取子域名( 之前的部分.domainname.com
),将其插入路径(%1)并将请求代理到默认 URL除非该目录不存在。
通过使用代理,该过程是透明的,用户认为他正在访问http://nonexistent.domainname.com,而他实际上看到的是http://default.domainname.com/。
答案3
我也在谷歌上发现了这个问题apache2 动态虚拟主机回退Luc 的回答对我解决问题帮助很大,但我仍然想展示我为实现目标所做的事情,主要是因为它涉及一些额外的工作,而且我认为它可能对未来的谷歌员工有帮助……
我的目标
- 为指向我的 VPS 的所有域和子域提供动态虚拟托管
foo.com
应提供与www.foo.com
- 将未知域名恢复为某种默认设置
- 除非不可用,
foo.com
否则将未知子域名回退到,而是回退到默认值www.foo.com
www
DNS
我有几个域(及其所有子域)指向我的 VPS,例如:
- foo.com
- 酒吧网
- foobar.com
文件系统
我有以下目录,域包含具有可用子域名称的目录,www 目录是必需的,但配置应该能够处理不存在的情况。 Localhost 用作默认后备:
/var
/www
/localhost
/foo.com
/www
/bar
/bar.com
/foo
测试
将我的目标转化为可测试的案例:
- foo.com应从foo.com/www
- www.foo.com应从foo.com/www
- bar.foo.com应从foo.com/bar
- foo.foo.com应从foo.com/www(foo.com/foo 不存在)
- 酒吧网应从本地主机(bar.com/www 不存在)
- www.bar.com应从本地主机(bar.com/www 不存在)
- foo.bar.com应从bar.com/foo
- bar.bar.com应从本地主机(bar.com/bar 不存在)
- foobar.com应从本地主机(foobar.com 不存在)
- www.foobar.com应从本地主机(foobar.com 不存在)
- foo.foobar.com应从本地主机(foobar.com 不存在)
解决方案
这使用:mod_rewrite
,mod_proxy_http
当然还有mod_vhost_alias
。
ServerName my.domain
ServerAdmin [email protected]
<VirtualHost *:80>
ServerName localhost
VirtualDocumentRoot /var/www/localhost
</VirtualHost>
<VirtualHost *:80>
ServerName sub.domain
ServerAlias *.*.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3 !-d
RewriteRule (.*) http://localhost/$1 [P]
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteCond /var/www/%2.%3/www !-d
RewriteRule (.*) http://localhost/$1 [P]
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteRule (.*) http://%2.%3/$1 [P]
</VirtualHost>
<VirtualHost *:80>
ServerName bare.domain
ServerAlias *.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/www
RewriteEngine on
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)$ [NC]
RewriteCond /var/www/%1.%2 !-d [OR]
RewriteCond /var/www/%1.%2/www !-d
RewriteRule (.*) http://localhost/$1 [P]
</VirtualHost>
这是如何工作的?定义了三个虚拟主机:
本地主机
localhost 为默认设置。所有无法解析的请求均由 localhost 处理。设置从 localhost 到任何域的符号链接就像将该网站设置为默认网站一样。
子域名
sub.domain vhost 以 的形式接收所有请求*.*.*
。默认情况下,所有请求均由/domain.com/sub
定义VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
。
倒退:
第一个通过代理本地主机网站RewriteRule
来处理未知域,例如domain.com
目录不存在。
RewriteRule
当domain.com/sub
和domain.com/www
目录都不存在时,第二个也代理到本地主机。
第三个RewriteRule
代理表示domain.com
不domain.com/sub
存在。我们知道domain.com/www
存在是因为第二个重写块。
裸域
bare.domain vhost 正在接收*.*
请求并为其提供服务/domain.com/www
当不存在时,将RewriteRule
代理到本地主机。domain.com
domain.com/www
^$%.*!!!
我有点难以理解所有这些标志,$
因此我将在这里解释它们:%
RewriteCond
RewriteRule
ServerAlias *.*.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
RewriteCond %{HTTP_HOST} ^(.*)\.(.*)\.(.*)$ [NC]
RewriteCond /var/www/%2.%3/%1 !-d
RewriteRule (.*) http://%2.%3/$1 [P]
*
中的只是ServerAlias
通配符。- 在
%n
的VirtualDocumentRoot
来自文档名称插值。 %n
第二个中的指的是第一个中的RewriteCond
选择,例如所请求域的各个部分。(.*)
RewriteCond
- 在也
%n
做RewriteRule
。 $1
中的指的是开头的RewriteRule
选择。它捕获从域到请求 URL 中的 的所有内容。任何查询字符串都会通过 被自动添加到 URL 中。(.*)
RewriteRule
?
mod_proxy
答案4
只需将其抛出,我便会获得与 RemyNL 相同的结果(减去本地主机重定向),但也包括直接连接时的 IP 地址:
<VirtualHost _default_:80>
RewriteEngine On
RewriteCond %{HTTPS} Off [OR]
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [NC,R=301,L]
</VirtualHost>
<VirtualHost _default_:443>
ServerName a.b.c.d
ServerAlias *.*.*.*
VirtualDocumentRoot /var/www/%0
</VirtualHost>
<VirtualHost _default_:443>
ServerName a.b.c
ServerAlias *.*.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3
</VirtualHost>
<VirtualHost _default_:443>
ServerName a.b
ServerAlias *.*
VirtualDocumentRoot /var/www/%-2.0.%-1.0/www
</VirtualHost>