Uso de VirtualDocumentRoot * solo * si existe una raíz de documento adecuada

8

Me gustaría configurar un entorno donde los hosts virtuales de Apache se puedan crear dinámicamente sin volver a cargar la configuración.

Puedo hacer esto con mod_vhost_alias , configuré mi host virtual predeterminado algo como esto

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

Eso funciona bien, pero si se solicita un nombre de host que no está configurado actualmente, aparece un error 404 No encontrado.

Lo que realmente me gustaría hacer es hacer que este VirtualHost se active solo si existe la raíz del documento, de lo contrario, intente que coincida con otro vhost (en otras palabras, haga que la presencia de VirtualDocumentRoot funcione de la misma manera que usar un ServerAlias)

Intenté hacer de este el segundo vhost, con el primer vhost solo manejando todas las solicitudes, pero esto no funcionó: las solicitudes de dominios donde se configuró un VirtualDocumentRoot estaban cayendo al vhost predeterminado.

Entonces, ¿cómo puedo tener vhosts configurados dinámicamente, pero con una alternativa a otro vhost para cualquiera que aún no esté configurado?

Paul Dixon
fuente

Respuestas:

7

Encontré una solución que me funciona.

Puedo usar un ErrorDocument para recoger el Error 404 en un script PHP. Al principio, eso parecía problemático. Si no hay DocumentRoot, ¿dónde vivirá el script?

Podría usar una URL para el mensaje de error, servido desde un dominio diferente. Sin embargo, en las pruebas no encontré ninguna forma de saber cuál era el dominio solicitado originalmente.

La respuesta fue Alias un directorio y servir los errores, así que mi vhost se ve así

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

Ahora, cuando se solicita un dominio no configurado, se invoca el script en /var/www/default/errors/notfound.php. Este script puede verificar $ _SERVER ['HTTP_HOST'] para ver qué dominio se solicitó. Si realmente está configurado, entonces tenemos un 404. normal. Si no está configurado, podemos mostrar un mensaje de error alternativo. En mi caso, es donde muestro una interfaz de usuario para ayudar a configurar el vhost.

Paul Dixon
fuente
¿No arrojaría un 404 como el estado de cada solicitud al valor predeterminado?
Kyle
1
El controlador de errores solo se invoca si VirtualDocumentRoot no existe (o si realmente era un 404, pero notfound.php puede notar la diferencia fácilmente).
Paul Dixon
3

Encontré tu pregunta cuando busqué en Google. Tuve exactamente el mismo problema y apliqué la solución como se describe en la respuesta de Paul . Sin embargo, para mi aplicación web compleja, no estaba contento con enrutar todas las solicitudes a través de una sola notfound.php.

Al final, logré solucionar el problema sin scripts externos, solo editando mi configuración VirtualHost.

Al principio, mi VirtualHost se configuró así:

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

Tenía URL como client1.domainname.com, client2.domainname.comywhatever.domainname.com

que resolvieron /mnt/ramdisk/www/cms-client1-development/, /mnt/ramdisk/www/cms-client2-development/y/mnt/ramdisk/www/cms-whatever-development/

Sin embargo, una URL como nonexistent.domainname.comme daría un 404 porque el directorio /mnt/ramdisk/www/cms-nonexistent-development/no existía. Quería que estos subdominios usaran el directorio/mnt/ramdisk/www/cms-default-development/

Lo arreglé usando ModRewritey 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>

Lo que esto hace es: tomar el subdominio (la parte anterior .domainname.com), insertarlo en la ruta (% 1) y las solicitudes de proxy a la URL predeterminada solo si el directorio no existe.

Al usar un proxy, el proceso es transparente y el usuario cree que está visitando http://nonexistent.domainname.com , mientras que en realidad ve el contenido de http://default.domainname.com/ .

Luc van Donkersgoed
fuente
3

También me encontré con esta pregunta buscando en Google apache2 Dynamic Vhost Fallback y la respuesta de Luc me ayudó mucho a resolver mi problema, pero todavía quiero mostrar lo que hice para lograr mis objetivos, principalmente porque implicó algunos trabajos adicionales y porque creo que podría ser útil para cualquier futuro googlers ...

Mis metas

  • Vhosting dinámico para todos los dominios y subdominios que apuntan a mi VPS
  • foo.com debe servir el mismo contenido que www.foo.com
  • reserva para dominios desconocidos a algún tipo de defecto
  • reserva para subdominios desconocidos de foo.coma www.foo.commenos que no wwwesté disponible, en su lugar recurra a la predeterminada

DNS

Tengo un par de dominios (y todos sus subdominios) apuntando a mi VPS, por ejemplo:

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

Sistema de archivos

Tengo los siguientes directorios, los dominios contienen directorios con los nombres de los subdominios disponibles, se requiere el directorio www, pero la configuración debería ser capaz de lidiar con la situación en la que no está presente. Localhost se utiliza como reserva predeterminada:

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

Pruebas

Traduciendo mis objetivos en casos comprobables:

  • foo.com debe ser servido desde foo.com/www
  • www.foo.com debe ser servido desde foo.com/www
  • bar.foo.com debe servirse desde foo.com/bar
  • foo.foo.com debe ser servido desde foo.com/www (foo.com/foo no existe)
  • bar.com debe servirse desde localhost (bar.com/www no existe)
  • www.bar.com debe ser servido desde localhost (bar.com/www no existe)
  • foo.bar.com debe ser servido desde bar.com/foo
  • bar.bar.com debe servirse desde localhost (bar.com/bar no existe)
  • foobar.com debe ser servido desde localhost (foobar.com no existe)
  • www.foobar.com debe ser servido desde localhost (foobar.com no existe)
  • foo.foobar.com debe ser servido desde localhost (foobar.com no existe)

La solución

Esto utiliza: y mod_rewrite, por mod_proxy_httpsupuesto 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 funciona esto? Hay tres hosts virtuales definidos:

localhost

El localhost sirve como predeterminado. Todas las solicitudes que no se pueden resolver son atendidas por localhost. Configurar un enlace simbólico desde localhost a cualquiera de sus dominios es como configurar ese sitio como predeterminado.

subdominio

El subdominio vhost está tomando todas las solicitudes en forma de *.*.*. Por defecto, todas las solicitudes se atienden /domain.com/subsegún lo definido porVirtualDocumentRoot /var/www/%-2.0.%-1.0/%-3 .

retroceder:

El primero RewriteRulese ocupa de dominios desconocidos, por ejemplo.domain.comel directorio no existe, mediante la representación del sitio web localhost.

El segundo RewriteRuletambién representa a localhost cuando tanto el domain.com/subcomo eldomain.com/www directorio directorio no están presentes.

El tercer RewriteRuleproxy de domain.comcuándo domain.com/subno existe. Sabemos domain.com/wwwque existe debido al segundo bloque de reescritura.

dominio desnudo

El vhost bare.domain está tomando el *.* solicitudes y las atiende/domain.com/www

Aquí el RewriteRuleproxy a localhost cuando domain.como domain.com/wwwno existe.

^ $%. * !!!

Tuve algunos problemas para envolver mi cabeza alrededor de todos aquellos $y %signos en el RewriteCondy RewriteRulepor lo que voy a explicar acerca de ellos aquí:

    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]
  • El *en elServerAlias son solo comodines.
  • En %nel VirtualDocumentRootson de la interpolación del nombre del documento .
  • En %nel segundo se RewriteCondrefieren a las selecciones (.*)del primeroRewriteCond , por ejemplo. Las partes del dominio solicitado.
  • El %nen elRewriteRule hacer también.
  • El $1en se RewriteRulerefiere a la selección (.*)al comienzo de RewriteRule. Que captura todo, desde el dominio hasta la ?URL de la solicitud. Cualquier cadena de consulta se agrega automáticamente a la url por mod_proxy.
RemyNL
fuente
1

Simplemente arrojándolo, obtengo el mismo resultado (menos la redirección localhost) que RemyNL, pero también incluye la dirección IP cuando me conecto directamente:

<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>
AyudaNeeder
fuente