仅当存在合适的文档根目录时才使用 VirtualDocumentRoot

仅当存在合适的文档根目录时才使用 VirtualDocumentRoot

我想建立一个可以动态创建 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.comclient2.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.comwww

DNS

我有几个域(及其所有子域)指向我的 VPS,例如:

  • foo.com
  • 酒吧网
  • foob​​ar.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 不存在)
  • foob​​ar.com应从本地主机(foobar.com 不存在)
  • www.foobar.com应从本地主机(foobar.com 不存在)
  • foo.foobar.com应从本地主机(foobar.com 不存在)

解决方案

这使用:mod_rewritemod_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目录不存在。

RewriteRuledomain.com/subdomain.com/www目录都不存在时,第二个也代理到本地主机。

第三个RewriteRule代理表示domain.comdomain.com/sub存在。我们知道domain.com/www存在是因为第二个重写块。

裸域

bare.domain vhost 正在接收*.*请求并为其提供服务/domain.com/www

当不存在时,将RewriteRule代理到本地主机。domain.comdomain.com/www

^$%.*!!!

我有点难以理解所有这些标志,$因此我将在这里解释它们:%RewriteCondRewriteRule

    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通配符。
  • %nVirtualDocumentRoot来自文档名称插值
  • %n第二个中的指的是第一个中的RewriteCond选择,例如所请求域的各个部分。(.*)RewriteCond
  • 在也%nRewriteRule
  • $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>

相关内容