Verwenden von VirtualDocumentRoot * nur *, wenn ein geeignetes Dokumentstammverzeichnis vorhanden ist

8

Ich möchte eine Umgebung einrichten, in der virtuelle Apache-Hosts dynamisch erstellt werden können, ohne die Konfiguration neu zu laden.

Ich kann dies mit mod_vhost_alias tun . Ich habe meinen virtuellen Standardhost so eingerichtet

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

Das funktioniert einwandfrei, aber wenn eine Anfrage nach einem Hostnamen gestellt wird, der derzeit nicht eingerichtet ist, wird der Fehler 404 Not Found angezeigt.

Was ich wirklich gerne tun würde, ist, diesen VirtualHost nur dann einzuschalten, wenn das Dokumentstammverzeichnis vorhanden ist. Andernfalls sollte er versuchen, mit einem anderen vhost übereinzustimmen (mit anderen Worten, das Vorhandensein des VirtualDocumentRoot funktioniert auf die gleiche Weise wie die Verwendung von a ServerAlias)

Ich habe versucht, dies zum zweiten vhost zu machen, wobei der erste vhost nur alle Anforderungen verarbeitet, aber dies hat nicht funktioniert - Anforderungen für Domänen, in denen ein VirtualDocumentRoot konfiguriert wurde, fielen auf den Standard-vhost durch.

Wie kann ich also vhosts dynamisch konfigurieren, aber mit einem Fallback auf einen anderen vhost für alle, die noch nicht konfiguriert sind?

Paul Dixon
quelle

Antworten:

7

Ich habe eine Problemumgehung gefunden, die für mich funktioniert.

Ich kann ein ErrorDocument verwenden , um den 404-Fehler in einem PHP-Skript zu ermitteln. Das schien zunächst problematisch. Wenn es kein DocumentRoot gibt, wo wird das Skript leben?

Ich könnte eine URL für die Fehlermeldung verwenden, die von einer anderen Domain geliefert wird. Beim Testen habe ich jedoch keine Möglichkeit gefunden, die ursprünglich angeforderte Domain zu ermitteln.

Die Antwort war, Alias ein Verzeichnis zu geben und die Fehler daraus zu machen, also sieht mein vhost so aus

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

Wenn nun eine nicht konfigurierte Domäne angefordert wird, wird stattdessen das Skript unter /var/www/default/errors/notfound.php aufgerufen. Dieses Skript kann $ _SERVER ['HTTP_HOST'] überprüfen, um festzustellen, welche Domain angefordert wurde. Wenn es tatsächlich konfiguriert ist, haben wir eine reguläre 404. Wenn es nicht konfiguriert ist, können wir eine alternative Fehlermeldung anzeigen. In meinem Fall zeige ich hier eine Benutzeroberfläche, um den vhost einzurichten.

Paul Dixon
quelle
Würde das nicht einen 404 als Status für jede Anfrage auf den Standardwert setzen?
Kyle
1
Die Fehlerbehandlungsroutine wird nur aufgerufen, wenn VirtualDocumentRoot nicht vorhanden ist (oder es sich tatsächlich um eine 404 handelt, die Datei notfound.php jedoch den Unterschied leicht erkennen kann).
Paul Dixon
3

Ich habe deine Frage beim Googeln gefunden. Ich hatte genau das gleiche Problem und wendete das Update an, wie in Pauls Antwort beschrieben . Für meine komplexe Web-App war ich jedoch nicht zufrieden damit, alle Anfragen über eine einzige zu leiten notfound.php.

Am Ende konnte ich das Problem ohne externe Skripte beheben, indem ich nur meine VirtualHost-Konfiguration bearbeitete.

Zuerst wurde mein VirtualHost folgendermaßen konfiguriert:

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

Ich hatte URLs mag client1.domainname.com, client2.domainname.comundwhatever.domainname.com

die beschlossen /mnt/ramdisk/www/cms-client1-development/, /mnt/ramdisk/www/cms-client2-development/und/mnt/ramdisk/www/cms-whatever-development/

Eine URL wie nonexistent.domainname.comwürde mir jedoch eine 404 geben, da das Verzeichnis /mnt/ramdisk/www/cms-nonexistent-development/nicht vorhanden war. Ich wollte, dass diese Subdomains das Verzeichnis verwenden/mnt/ramdisk/www/cms-default-development/

Ich habe dies mit ModRewriteund behoben 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>

Dies bedeutet: Nehmen Sie die Subdomain (den vorherigen Teil .domainname.com), fügen Sie diese in den Pfad (% 1) ein und senden Sie Proxy-Anforderungen an die Standard-URL nur, wenn das Verzeichnis nicht vorhanden ist.

Durch die Verwendung eines Proxys ist der Prozess transparent und der Benutzer glaubt, http://nonexistent.domainname.com zu besuchen , während er den Inhalt von http://default.domainname.com/ anzeigt .

Luc van Donkers ging
quelle
3

Ich bin auch auf diese Frage gestoßen, die nach apache2 dynamic vhost fallback gegoogelt hat, und Lucs Antwort hat mir sehr bei der Lösung meines Problems geholfen, aber ich möchte immer noch zeigen, was ich getan habe, um meine Ziele zu erreichen, hauptsächlich, weil es einige zusätzliche Arbeiten beinhaltete und weil ich es denke könnte für zukünftige Googler hilfreich sein ...

Meine Ziele

  • dynamisches vhosting für alle Domains und Subdomains, die auf meinen VPS verweisen
  • foo.com sollte den gleichen Inhalt wie dienen www.foo.com
  • Fallback für unbekannte Domains auf eine Art Standard
  • Fallback für unbekannte Subdomains von foo.combis, www.foo.comsofern das wwwnicht nicht verfügbar ist, stattdessen Fallback auf Standard

DNS

Ich habe ein paar Domains (und alle ihre Subdomains), die auf meinen VPS zeigen, zum Beispiel:

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

Dateisystem

Ich habe die folgenden Verzeichnisse, Domänen enthalten Verzeichnisse mit den Namen der verfügbaren Subdomänen, das Verzeichnis www ist erforderlich, aber die Konfiguration sollte in der Lage sein, mit der Situation umzugehen, in der es nicht vorhanden ist. Localhost wird als Standard-Fallback verwendet:

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

Tests

Meine Ziele in überprüfbare Fälle übersetzen:

  • foo.com sollte von foo.com/www aus bedient werden
  • www.foo.com sollte von foo.com/www aus bedient werden
  • bar.foo.com sollte von foo.com/bar aus bedient werden
  • foo.foo.com sollte von foo.com/www aus bedient werden (foo.com/foo existiert nicht)
  • bar.com sollte von localhost aus bedient werden (bar.com/www existiert nicht)
  • www.bar.com sollte von localhost aus bedient werden (bar.com/www existiert nicht)
  • foo.bar.com sollte von bar.com/foo aus bedient werden
  • bar.bar.com sollte von localhost aus bedient werden (bar.com/bar existiert nicht)
  • foobar.com sollte von localhost aus bedient werden (foobar.com existiert nicht)
  • www.foobar.com sollte von localhost aus bedient werden (foobar.com existiert nicht)
  • foo.foobar.com sollte von localhost aus bedient werden (foobar.com existiert nicht)

Die Lösung

Diese verwendet: mod_rewrite, mod_proxy_httpund natürlich 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>

Wie funktioniert das? Es sind drei virtuelle Hosts definiert:

localhost

Der localhost dient als Standard. Alle Anfragen, die nicht auflösbar sind, werden von localhost bearbeitet. Das Einrichten eines Symlinks von localhost zu einer Ihrer Domains entspricht dem standardmäßigen Einrichten dieser Site.

Subdomain

Die Subdomain vhost nimmt alle Anfragen in Form von entgegen *.*.*. Standardmäßig werden alle Anforderungen wie von /domain.com/subdefiniert VirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3.

Zurückfallen:

Der erste RewriteRulekümmert sich um unbekannte Domänen, z. domain.comVerzeichnis existiert nicht, indem Sie die localhost-Website vertreten.

Der zweite ist RewriteRuleauch ein Proxy für localhost, wenn sowohl das domain.com/subals auch das domain.com/wwwVerzeichnis nicht vorhanden sind.

Der dritte RewriteRuleStellvertreter für domain.comwann domain.com/subexistiert nicht. Wir wissen, domain.com/wwwdass es aufgrund des zweiten Umschreibungsblocks existiert.

bare.domain

Der bare.domain vhost nimmt die *.*Anfragen entgegen und bedient sie/domain.com/www

Hier wird der RewriteRuleWillensvertreter zu localhost, wenn domain.comoder domain.com/wwwnicht vorhanden.

^ $%. * !!!

Ich hatte einige Probleme, meinen Kopf um all diese $und %Zeichen in der zu wickeln, RewriteCondund RewriteRuledeshalb werde ich sie hier erklären:

    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]
  • Die *in der ServerAliassind nur Platzhalter.
  • Die %nin der VirtualDocumentRootsind aus dem Dokumentnamen Interpolation .
  • Die %nin der zweiten RewriteCondbeziehen sich auf die Auswahl (.*)aus der ersten RewriteCond, z. die Teile der angeforderten Domain.
  • Die %nin der RewriteRulees auch tun.
  • Das $1in RewriteRulebezieht sich auf die Auswahl (.*)am Anfang des RewriteRule. Welches erfasst alles von der Domain bis zur ?in der Anfrage URL. Jeder Querystring wird automatisch zur URL von hinzugefügt mod_proxy.
RemyNL
quelle
1

Wenn ich es einfach rauswerfe, erhalte ich das gleiche Ergebnis (abzüglich der Localhost-Umleitung) wie RemyNL, aber auch die IP-Adresse, wenn ich eine direkte Verbindung herstelle:

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