Назад | Перейти на главную страницу

Использование VirtualDocumentRoot * только * при наличии подходящего корня документа

Я хотел бы создать среду, в которой виртуальные хосты Apache могут создаваться динамически без перезагрузки конфигурации.

Я могу сделать это с mod_vhost_alias, Я настроил свой виртуальный хост по умолчанию примерно так

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

Это работает нормально, но если делается запрос для имени хоста, которое в настоящее время не настроено, я получаю ошибку 404 Not Found.

Что бы я действительно хотел бы сделать только этот VirtualHost только если корень документа существует, в противном случае пусть он попытается сопоставить другой виртуальный хост (другими словами, сделайте наличие VirtualDocumentRoot работают так же, как при использовании ServerAlias)

Я попытался сделать это вторым виртуальным хостом, причем первый виртуальный хост просто обрабатывал все запросы, но это не сработало - запросы для доменов, для которых был настроен VirtualDocumentRoot, пропускались к виртуальному хосту по умолчанию.

Итак, как я могу динамически сконфигурировать vhosts, но с откатом на другой vhost для любого, который еще не настроен?

Я нашел обходной путь, который мне подходит.

Я могу использовать ErrorDocument чтобы обнаружить ошибку 404 в сценарии PHP. Поначалу это казалось проблематичным. Если нет DocumentRoot, где будет жить скрипт?

Я мог бы использовать URL-адрес для сообщения об ошибке, полученного из другого домена. Однако во время тестирования я не нашел способа узнать, какой домен был запрошен изначально.

Ответ был на Псевдоним каталог и обрабатывать ошибки из него, поэтому мой vhost выглядит так

<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. Если он не настроен, мы можем отобразить другое сообщение об ошибке. В моем случае здесь я показываю пользовательский интерфейс, который помогает настроить виртуальный хост.

Я нашел ваш вопрос, когда погуглил. У меня была точно такая же проблема, и я применил исправление, как описано в Ответ Павла. Однако для моего сложного веб-приложения я не был доволен маршрутизацией всех запросов через один 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/

Однако 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/.

Я также столкнулся с этим вопросом в Google для apache2 динамический резервный хост vhost и ответ Люка очень помог мне в решении моей проблемы, но я все еще хочу показать, что я сделал для достижения своих целей, главным образом потому, что это потребовало дополнительных работ и потому, что я думаю, что это может быть полезно для любых будущих гуглеров ...

Мои цели

  • динамический хостинг для всех доменов и поддоменов, указывающих на мой VPS
  • foo.com должен обслуживать тот же контент, что и www.foo.com
  • откат для неизвестных доменов на какое-то значение по умолчанию
  • резервный вариант для неизвестных субдоменов foo.com к www.foo.com если только www недоступно, вместо этого вернуться к умолчанию

DNS

У меня есть несколько доменов (и все их поддомены), указывающих на мой VPS, например:

  • foo.com
  • bar.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 должен подаваться из localhost (bar.com/www не существует)
  • www.bar.com должен подаваться из localhost (bar.com/www не существует)
  • foo.bar.com должен подаваться из bar.com/foo
  • bar.bar.com должен подаваться из localhost (bar.com/bar не существует)
  • foobar.com должен подаваться из localhost (foobar.com не существует)
  • www.foobar.com должен подаваться из localhost (foobar.com не существует)
  • foo.foobar.com должен подаваться из localhost (foobar.com не существует)

Решение

Это использует: mod_rewrite, mod_proxy_http и конечно 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

По умолчанию используется localhost. Все неразрешимые запросы обслуживаются localhost. Настройка символической ссылки с localhost на любой из ваших доменов аналогична настройке этого сайта по умолчанию.

субдомен

Поддомен vhost принимает все запросы в виде *.*.*. По умолчанию все запросы обслуживаются из /domain.com/sub как определено VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3.

отступать:

Первый RewriteRule заботится о неизвестных доменах, например. domain.com каталог не существует, проксируя веб-сайт localhost.

Второй RewriteRule также прокси к localhost, когда оба domain.com/sub и domain.com/www справочников нет.

Третий RewriteRule прокси для domain.com когда domain.com/sub не существует. Мы знаем domain.com/www существует из-за второго блока перезаписи.

bare.domain

Виртуальный хост bare.domain принимает *.*просит и обслуживает их /domain.com/www

Здесь RewriteRule будет прокси на localhost, когда 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 относится к выбору (.*) в начале RewriteRule. Которая захватывает все, от домена до ? в URL-адресе запроса. Любая строка запроса автоматически добавляется к URL-адресу mod_proxy.

Просто выбросив его туда, я получаю тот же результат (без перенаправления localhost), что и 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>