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

8

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

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

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

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

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

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

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

Пол Диксон
источник

Ответы:

7

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

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

Пол Диксон
источник
Разве это не выбрасывает 404 в качестве статуса для каждого запроса по умолчанию?
Кайл
1
Обработчик ошибок вызывается только в том случае, если VirtualDocumentRoot не существует (или на самом деле это был 404, но notfound.php может легко определить разницу).
Пол Диксон
3

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

Люк ван Донкерсгоед
источник
3

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

Мои цели

  • динамический хостинг для всех доменов и поддоменов, указывающих на мой 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 должен обслуживаться с локального хоста (foobar.com не существует)
  • www.foobar.com должен обслуживаться с localhost (foobar.com не существует)
  • foo.foobar.com должен обслуживаться с локального хоста (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 на любой из ваших доменов аналогична настройке этого сайта по умолчанию.

sub.domain

Sub.domain 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.
RemyNL
источник
1

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