Usando VirtualDocumentRoot * somente * se existir uma raiz de documento adequada

8

Eu gostaria de configurar um ambiente em que os hosts virtuais do Apache possam ser criados dinamicamente sem recarregar a configuração.

Eu posso fazer isso com mod_vhost_alias , configurei meu host virtual padrão algo como isto

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

Isso funciona muito bem, mas se uma solicitação for feita para um nome de host que não está configurado no momento, eu recebo um erro 404 Não encontrado.

O que eu realmente gostaria de fazer é ter esse chute do VirtualHost apenas se a raiz do documento existir, caso contrário, tente corresponder a outro vhost (em outras palavras, faça com que a presença do VirtualDocumentRoot funcione da mesma maneira que o uso de um ServerAlias)

Tentei fazer deste o segundo vhost, com o primeiro vhost apenas manipulando todas as solicitações, mas isso não funcionou - as solicitações de domínios em que um VirtualDocumentRoot foi configurado estavam caindo no vhost padrão.

Então, como posso configurar vhosts dinamicamente, mas com um fallback para outro vhost para qualquer um que ainda não esteja configurado?

Paul Dixon
fonte

Respostas:

7

Encontrei uma solução alternativa que funciona para mim.

Eu posso usar um ErrorDocument para pegar o erro 404 em um script PHP. A princípio, isso parecia problemático. Se não houver DocumentRoot, onde o script viverá?

Eu poderia usar um URL para a mensagem de erro, veiculada em um domínio diferente. No entanto, nos testes, não encontrei maneira de saber qual era o domínio originalmente solicitado.

A resposta foi Alias um diretório e servir os erros a partir disso, então meu vhost se parece com isso

<VirtualHost *>
  UseCanonicalName Off
  VirtualDocumentRoot /var/www/sandboxes/domains/%0
  ServerName catchall.host
  Alias /errors /var/www/default/errors/
  ErrorDocument 404 /errors/notfound.php
</VirtualHost>

Agora, quando um domínio não configurado é solicitado, o script em /var/www/default/errors/notfound.php é chamado. Este script pode verificar $ _SERVER ['HTTP_HOST'] para ver qual domínio foi solicitado. Se ele estiver realmente configurado, teremos um 404 regular. Se não estiver configurado, podemos exibir uma mensagem de erro alternativa. No meu caso, é onde eu mostro uma interface do usuário para ajudar a configurar o vhost.

Paul Dixon
fonte
Isso não lançaria um 404 como o status de cada solicitação para o padrão?
Kyle
1
O manipulador de erros só é chamado se o VirtualDocumentRoot não existir (ou realmente era um 404, mas o notfound.php pode facilmente dizer a diferença).
Paul Dixon
3

Encontrei sua pergunta no Google. Eu tive exatamente o mesmo problema e apliquei a correção conforme descrito na resposta de Paulo . No entanto, para meu aplicativo Web complexo, eu não estava feliz com o roteamento de todas as solicitações através de uma única notfound.php.

No final, consegui corrigir o problema sem scripts externos, apenas editando minha configuração do VirtualHost.

Inicialmente, meu VirtualHost foi configurado assim:

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

Eu tinha URLs gosto client1.domainname.com, client2.domainname.comewhatever.domainname.com

que resolveu /mnt/ramdisk/www/cms-client1-development/, /mnt/ramdisk/www/cms-client2-development/e/mnt/ramdisk/www/cms-whatever-development/

No entanto, uma URL como nonexistent.domainname.comme daria um 404 porque o diretório /mnt/ramdisk/www/cms-nonexistent-development/não existia. Eu queria que esses subdomínios usassem o diretório/mnt/ramdisk/www/cms-default-development/

Corrigi isso usando ModRewritee 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>

O que isso faz é: pegue o subdomínio (a parte anterior .domainname.com), insira-o no caminho (o% 1) e solicite proxy ao URL padrão apenas se o diretório não existir.

Ao usar um proxy, o processo é transparente e o usuário pensa que está visitando http://nonexistent.domainname.com , enquanto ele realmente visualiza o conteúdo em http://default.domainname.com/ .

Luc van Donkersgoed
fonte
3

Eu também me deparei com essa pergunta pesquisando no Google sobre apache2 dynamic vhost fallback e a resposta de Luc me ajudou muito na solução do meu problema, mas ainda quero mostrar o que fiz para alcançar meus objetivos, principalmente porque envolveu alguns trabalhos extras e porque acho que pode ser útil para futuros googlers ...

Os meus objetivos

  • vhosting dinâmico para todos os domínios e subdomínios apontando para o meu VPS
  • foo.com deve veicular o mesmo conteúdo que www.foo.com
  • fallback para domínios desconhecidos para algum tipo de padrão
  • fallback para subdomínios desconhecidos de foo.coma www.foo.com, a menos que não wwwesteja disponível, fallback para o padrão

DNS

Eu tenho alguns domínios (e todos os seus subdomínios) apontando para o meu VPS, por exemplo:

  • foo.com
  • bar.com
  • foobar.com

Sistema de arquivo

Eu tenho os seguintes diretórios, os domínios contêm diretórios com os nomes dos subdomínios disponíveis, o diretório www é necessário, mas a configuração deve ser capaz de lidar com a situação em que não está presente. Localhost é usado como fallback padrão:

/var
  /www
    /localhost
    /foo.com
       /www
       /bar
    /bar.com
       /foo

Testes

Traduzindo meus objetivos em casos testáveis:

  • foo.com deve ser veiculado em foo.com/www
  • www.foo.com deve ser veiculado em foo.com/www
  • bar.foo.com deve ser veiculado em foo.com/bar
  • foo.foo.com deve ser veiculado em foo.com/www (foo.com/foo não existe)
  • bar.com deve ser veiculado no localhost (bar.com/www não existe)
  • www.bar.com deve ser veiculado no localhost (bar.com/www não existe)
  • foo.bar.com deve ser veiculado em bar.com/foo
  • bar.bar.com deve ser veiculado no localhost (bar.com/bar não existe)
  • foobar.com deve ser veiculado no localhost (foobar.com não existe)
  • www.foobar.com deve ser veiculado no localhost (foobar.com não existe)
  • foo.foobar.com deve ser veiculado a partir do host local (foobar.com não existe)

A solução

Isto usa: mod_rewrite, mod_proxy_httpe é claro 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>

Como é que isso funciona? Existem três hosts virtuais definidos:

localhost

O host local serve como padrão. Todas as solicitações que não são resolvíveis são atendidas pelo host local. A configuração de um link simbólico do host local para qualquer um dos seus domínios é como configurar o site como padrão.

sub.domínio

O sub.domain vhost está recebendo todas as solicitações no formato de *.*.*. Por padrão, todas as solicitações são atendidas /domain.com/subconforme definido por VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3.

cair pra trás:

O primeiro RewriteRulecuida de domínios desconhecidos, por exemplo. domain.comO diretório não existe, fazendo proxy do site localhost.

O segundo RewriteRuletambém procura proxies para o host local quando domain.com/subos domain.com/wwwdiretórios e os não estão presentes.

Os terceiros RewriteRuleproxies para domain.comquando domain.com/subnão existe. Sabemos domain.com/wwwque existe por causa do segundo bloco de reescrita.

bare.domain

O vhost bare.domain está recebendo as *.*solicitações e as atende/domain.com/www

Aqui, o RewriteRuleproxy procurará o host local quando domain.comou domain.com/wwwnão existir.

^ $%. * !!!

Eu tive alguns problemas envolvendo minha cabeça em torno de todos aqueles $e %sinais no RewriteConde RewriteRulepor isso vou explicar sobre eles aqui:

    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]
  • Os *no ServerAliassão apenas curingas.
  • O %nno VirtualDocumentRootsão do nome do documento de interpolação .
  • O %nsegundo RewriteCondrefere-se às seleções (.*)do primeiro RewriteCond, por exemplo. as partes do domínio solicitado.
  • O %nno RewriteRulefazer também.
  • O $1no RewriteRulerefere-se à seleção (.*)no início do RewriteRule. Que captura tudo, desde o domínio até o ?URL da solicitação. Qualquer string de consulta é automaticamente adicionada ao URL por mod_proxy.
RemyNL
fonte
1

Apenas lançando-o por aí, recebo o mesmo resultado (menos o redirecionamento de host local) que o RemyNL, mas também incluindo o endereço IP ao conectar diretamente:

<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
fonte