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


8

我想建立一个可以动态创建Apache虚拟主机而无需重新加载配置的环境。

我可以使用mod_vhost_alias做到这一点,我可以像这样设置默认的虚拟主机

<VirtualHost *>
  UseCanonicalName Off
  VirtualDocumentRoot /var/www/sandboxes/domains/%0
  ServerName catchall.host
</VirtualHost>

效果很好,但是如果请求当前未设置的主机名,则会收到404 Not Found错误。

真正想做的是仅在文档根目录存在的情况下启动VirtualHost ,否则请尝试匹配另一个虚拟主机(换句话说,使VirtualDocumentRoot的存在与使用虚拟主机相同) ServerAlias)

我尝试将其作为第二个虚拟主机,而第一个虚拟主机仅处理所有请求,但这没有用-对配置了VirtualDocumentRoot的域的请求落入默认的虚拟主机。

因此,如何动态配置虚拟主机,但对于尚未配置的任何虚拟主机又回退到另一个虚拟主机?

Answers:


7

我找到了对我有效的解决方法。

我可以使用ErrorDocument在PHP脚本中获取404错误。起初,这似乎有问题。如果没有DocumentRoot,脚本将存放在哪里?

我可以使用来自其他域的错误消息的URL。但是,在测试中,我没有办法知道最初请求的域是什么。

答案是给Alias一个目录并提供其中的错误,所以我的虚拟主机看起来像这样

<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来帮助设置虚拟主机的地方。


这样是否会将404作为默认请求的状态发送?
凯尔2012年

1
仅当VirtualDocumentRoot不存在(或者它确实是404,但notfound.php可以轻松分辨出区别)时才调用错误处理程序。
保罗·迪克森

3

谷歌搜索时发现了您的问题。我遇到了完全相同的问题,并按照Paul的回答中所述进行了修复。但是,对于我复杂的网络应用程序,我不满意通过一个路由所有请求notfound.php

最后,仅通过编辑VirtualHost配置,我就无需使用外部脚本即可解决此问题。

首先,我的VirtualHost配置如下:

<VirtualHost *:80>
    Usecanonicalname Off
    Virtualdocumentroot /mnt/ramdisk/www/cms-%-3.0-development/
</VirtualHost>

我有类似的网址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/

但是,类似URL这样的URL nonexistent.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的回答对解决我的问题有很大帮助,但是我仍然想展示我为实现目标所做的工作,主要是因为它涉及一些额外的工作,而且我认为对将来的任何Google员工可能会有帮助...

我的目标

  • 指向我的VPS的所有域和子域的动态虚拟主机
  • foo.com 应该与 www.foo.com
  • 将未知域回退到某种默认值
  • 回退为未知的子域foo.comwww.foo.com,除非www是不可用的,退回到默认,而不是

域名解析

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

  • foo.com
  • bar.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提供
  • 应该从foo.com/bar提供bar.foo.com
  • foo.foo.com应该通过foo.com/www提供(foo.com/foo不存在)
  • bar.com应该从服务本地主机(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和ofcourse mod_vhost_alias

ServerName my.domain
ServerAdmin admin@my.domain

<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进行服务。设置从本地主机到任何域的符号链接就像将该站点设置为默认站点一样。

子域

sub.domain虚拟主机以的形式接受所有请求*.*.*。默认情况下,所有请求均由/domain.com/sub定义VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3

倒退:

第一个RewriteRule负责未知域,例如。domain.com目录通过代理localhost网站不存在。

RewriteRuledomain.com/subdomain.com/www目录都不存在时,第二个也代理localhost 。

何时的第三个RewriteRule代理不存在。我们知道确实存在,因为第二个重写块。domain.comdomain.com/subdomain.com/www

裸域

nude.domain虚拟主机正在接受*.*请求并为其提供服务/domain.com/www

在此处,RewriteRuledomain.comdomain.com/www不存在时,will将代理到localhost 。

^ $%。* !!!

我在将所有这些标志$%标志包裹起来时遇到一些麻烦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

1

只是将它扔在那里,我得到的结果与RemyNL相同(减去localhost重定向),但是在直接连接时还包括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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.