Utilisation de VirtualDocumentRoot * uniquement * s'il existe une racine de document appropriée

8

Je voudrais mettre en place un environnement où les hôtes virtuels Apache peuvent être créés dynamiquement sans recharger la configuration.

Je peux le faire avec mod_vhost_alias , j'ai configuré mon hôte virtuel par défaut quelque chose comme ça

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

Cela fonctionne très bien, mais si une demande est faite pour un nom d'hôte qui n'est pas actuellement configuré, j'obtiens une erreur 404 Not Found.

Ce que j'aimerais vraiment faire, c'est que ce VirtualHost ne démarre que si la racine du document existe, sinon essayez de faire correspondre un autre vhost (en d'autres termes, faites en sorte que la présence du VirtualDocumentRoot fonctionne de la même manière que l'utilisation d'un ServerAlias)

J'ai essayé d'en faire le deuxième vhost, le premier vhost ne gérant que toutes les demandes, mais cela n'a pas fonctionné - les demandes de domaines où un VirtualDocumentRoot était configuré tombaient vers le vhost par défaut.

Alors, comment puis-je avoir des vhosts configurés dynamiquement, mais avec un retour à un autre vhost pour ceux qui ne sont pas encore configurés?

Paul Dixon
la source

Réponses:

7

J'ai trouvé une solution de contournement qui fonctionne pour moi.

Je peux utiliser un ErrorDocument pour récupérer l'erreur 404 dans un script PHP. Au début, cela semblait problématique. S'il n'y a pas DocumentRoot, où vivra le script?

Je pourrais utiliser une URL pour le message d'erreur, diffusée depuis un autre domaine. Cependant, lors des tests, je n'ai trouvé aucun moyen de savoir quel était le domaine initialement demandé.

La réponse a été d' alias un répertoire et de servir les erreurs, donc mon vhost ressemble à ceci

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

Désormais, lorsqu'un domaine non configuré est demandé, le script à /var/www/default/errors/notfound.php est appelé à la place. Ce script peut vérifier $ _SERVER ['HTTP_HOST'] pour voir quel domaine a été demandé. S'il est réellement configuré, nous en avons un 404. Si ce n'est pas configuré, nous pouvons afficher un autre message d'erreur. Dans mon cas, c'est là que je montre une interface utilisateur pour aider à configurer le vhost.

Paul Dixon
la source
Est-ce que cela ne jetterait pas un 404 comme statut pour chaque demande par défaut?
Kyle
1
Le gestionnaire d'erreurs n'est invoqué que si VirtualDocumentRoot n'existe pas (ou s'il s'agissait vraiment d'un 404, mais le notfound.php peut facilement faire la différence).
Paul Dixon
3

J'ai trouvé votre question lors de la recherche sur Google. J'ai eu exactement le même problème et j'ai appliqué le correctif tel que décrit dans la réponse de Paul . Cependant, pour mon application Web complexe, je n'étais pas satisfait du routage de toutes les demandes via une seule notfound.php.

Au final, j'ai réussi à résoudre le problème sans scripts externes, uniquement en modifiant ma configuration VirtualHost.

Au début, mon VirtualHost était configuré comme ceci:

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

J'avais des URL comme client1.domainname.com, client2.domainname.cometwhatever.domainname.com

qui a résolu à /mnt/ramdisk/www/cms-client1-development/, /mnt/ramdisk/www/cms-client2-development/et/mnt/ramdisk/www/cms-whatever-development/

Cependant, une URL comme nonexistent.domainname.comme donnerait un 404 car le répertoire /mnt/ramdisk/www/cms-nonexistent-development/n'existait pas. Je voulais que ces sous-domaines utilisent le répertoire/mnt/ramdisk/www/cms-default-development/

J'ai corrigé cela en utilisant ModRewriteet 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>

Ce que cela fait, c'est: saisir le sous-domaine (la partie précédente .domainname.com), l'insérer dans le chemin d'accès (le% 1) et les requêtes proxy vers l'URL par défaut uniquement si le répertoire n'existe pas.

En utilisant un proxy, le processus est transparent et l'utilisateur pense qu'il visite http://nonexistent.domainname.com , alors qu'il affiche en fait le contenu de http://default.domainname.com/ .

Luc van Donkersgoed
la source
3

Je suis également tombé sur cette question en recherchant Google pour le remplacement dynamique d'Apache2 vhost et la réponse de Luc m'a beaucoup aidé à résoudre mon problème, mais je veux toujours montrer ce que j'ai fait pour atteindre mes objectifs, principalement parce que cela impliquait des travaux supplémentaires et parce que je pense que cela pourrait être utile pour tous les futurs googleurs ...

Mes objectifs

  • vhosting dynamique pour tous les domaines et sous-domaines pointant vers mon VPS
  • foo.com devrait servir le même contenu que www.foo.com
  • repli des domaines inconnus vers une sorte de défaut
  • repli pour les sous-domaines inconnus de à foo.comà www.foo.commoins que le wwwne soit pas disponible, repli sur défaut par défaut

DNS

J'ai quelques domaines (et tous leurs sous-domaines) pointant vers mon VPS, par exemple:

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

Système de fichiers

J'ai les répertoires suivants, les domaines contiennent des répertoires avec les noms des sous-domaines disponibles, le répertoire www est requis, mais la configuration devrait être capable de faire face à la situation où il n'est pas présent. Localhost est utilisé comme solution de repli par défaut:

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

Les tests

Traduire mes objectifs en cas testables:

  • foo.com doit être servi à partir de foo.com/www
  • www.foo.com doit être servi à partir de foo.com/www
  • bar.foo.com devrait être servi à partir de foo.com/bar
  • foo.foo.com doit être servi à partir de foo.com/www (foo.com/foo n'existe pas)
  • bar.com doit être servi depuis localhost (bar.com/www n'existe pas)
  • www.bar.com doit être servi depuis localhost (bar.com/www n'existe pas)
  • foo.bar.com doit être servi à partir de bar.com/foo
  • bar.bar.com doit être servi depuis localhost (bar.com/bar n'existe pas)
  • foobar.com doit être servi depuis localhost (foobar.com n'existe pas)
  • www.foobar.com doit être servi depuis localhost (foobar.com n'existe pas)
  • foo.foobar.com doit être servi depuis localhost (foobar.com n'existe pas)

La solution

Cette utilisation: mod_rewrite, mod_proxy_httpet ofcourse 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>

Comment cela marche-t-il? Trois hôtes virtuels sont définis:

localhost

L'hôte local sert par défaut. Toutes les demandes qui ne peuvent pas être résolues sont traitées par localhost. La configuration d'un lien symbolique depuis localhost vers l'un de vos domaines revient à configurer ce site par défaut.

sous-domaine

Le sous-domaine vhost prend toutes les demandes sous la forme de *.*.*. Par défaut, toutes les demandes sont traitées /domain.com/subcomme défini par VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3.

se retirer:

Le premier RewriteRuleprend en charge les domaines inconnus, par exemple. domain.comn'existe pas, en procurant le proxy au site Web localhost.

Le second RewriteRulesert également de proxy à localhost lorsque domain.com/subles domain.com/wwwrépertoires et ne sont pas présents.

Le troisième RewriteRuleproxy à domain.comquand domain.com/subn'existe pas. Nous savons domain.com/wwwqu'il existe en raison du deuxième bloc de réécriture.

bare.domain

Le vhost bare.domain prend les *.*demandes et les sert/domain.com/www

Ici, le RewriteRuleproxy vers localhost quand domain.comou domain.com/wwwn'existe pas.

^ $%. * !!!

J'ai eu du mal à enrouler ma tête autour de tous ces signes $et %signes dans le RewriteCondet RewriteRuleje vais donc expliquer à leur sujet ici:

    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]
  • L' *en ServerAliassont juste des jokers.
  • Les %ndans le VirtualDocumentRootsont issus de l' interpolation du nom du document .
  • Le %ndans le second se RewriteCondréfère aux sélections (.*)du premier RewriteCond, par exemple. les parties du domaine demandé.
  • Le %ndans le RewriteRulefaire aussi.
  • Le $1dans le RewriteRulefait référence à la sélection (.*)au début du RewriteRule. Qui capture tout depuis le domaine jusqu'à l' ?URL de demande. Toute chaîne de requête est automatiquement ajoutée à l'URL par mod_proxy.
RemyNL
la source
1

Juste en le jetant là-bas, j'obtiens le même résultat (moins la redirection localhost) que RemyNL, mais en incluant également l'adresse IP lors de la connexion directe:

<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
la source