Cover Image

Sette opp Apache 2.4 for mass virtual hosting

 Thu 2018-01-11    Web

Denne posten tar for seg oppsett av det som kalles "mass virtual hosting" på Apache.

Det finnes forskjellige måter å gjøre dette på også, og måten jeg gjør det på er sikkert ikke den beste, men det funker for meg og har gjort det lenge.

Min config, som ikke er min i det hele tatt, men tilpasset mine behov basert på en config for et webcluster i en tidligere jobb, benytter seg av Apaches mod_rewrite. Configen ble brukt på Apache 2.0 på webclusteret, og tilpasset Apache 1.3 for min egen server. Etterhvert ble Apache oppgradert til 2.0, 2.2 og sist til 2.4, og configen har blitt tilpasset i takt med oppgraderingene.

Apache har også en egen modul for nettopp dette, mod_vhost_alias, som i følge dokumentasjonen skal gjøre akkurat det samme denne configen gjør, men jeg har ikke blitt helt venner med mod_vhost_alias siden den legger føringer for mappestruktur og ikke fungerer med subdomener. Configen under gir meg helt frie tøyler til å lage akkurat den mappestrukturen jeg vil, og den fungerer med subdomener helt automagisk.

APACHE

Dette er altså min auto-vhost-config for Apache, som jeg har brukt i 12 år. Den er noe dyr i drift siden den er basert på et lass rewrite-statements, men den funker bra og har blitt oppdatert fra Apache 1.3 til 2.0 til 2.2 til 2.4 gjennom årenes løp.

Siste versjon for Apache 2.4 ser slik ut:

httpd-autovhost.conf:

#
# Use name-based virtual hosting.
#
#NameVirtualHost *:80 # Deprecated in Apache 2.4

<Virtualhost *:80>
 <Directory /www/>
# AllowOverride FileInfo AuthConfig Limit Indexes Options
 AllowOverride All
 Options MultiViews Includes FollowSymlinks ExecCGI Indexes
 <Limit GET POST OPTIONS>
 Require all granted
 </Limit>
 <LimitExcept GET POST OPTIONS>
 Require all granted
 </LimitExcept>
 </Directory>

Alias /error/ "/usr/local/www/apache24/error/"

<Directory "/usr/local/www/apache24/error/">
 AllowOverride None
 Options IncludesNoExec
 AddOutputFilter Includes html
 AddHandler type-map var
 Require all granted
 LanguagePriority en cs de es fr it ja ko nl pl pt-br ro sv tr
 ForceLanguagePriority Prefer Fallback
 </Directory>

 ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
 ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var
 ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var
 ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var
 ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var
 ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var
 ErrorDocument 410 /error/HTTP_GONE.html.var
 ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var
 ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var
 ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
 ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var
 ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
 ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var
 ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var
 ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var
 ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var
 ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var

# Activate the magic
RewriteEngine On

# Remove the Vary header for MSIE, since it doesn't support pipelining. Not sure about recent versions, and old versions shouldn't be used. We don't want to support old versions anyway.
Header unset Vary

RewriteCond %{REQUEST_METHOD} !^(GET|POST|HEAD)$
RewriteRule .* - [F]

# We need to force mime types for cgi scripts and php scripts.
# We create our own function variable called "lowercase"

RewriteMap lowercase int:tolower

### CGI

# Deal with CGIs -- force a MIME type
RewriteCond %{REQUEST_URI} ^/cgi-bin/

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+)$ [NC]

# Check if there is a symlink for domain in /www
RewriteCond /www/${lowercase:%2} -l

# Do the redirect
RewriteRule ^/cgi-bin/(.*)$ /www/${lowercase:%2}/htdocs/cgi-bin/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%2}/htdocs,T=application/x-httpd-cgi,L]

# Deal with errors if something isn't found
RewriteCond %{REQUEST_URI} ^/error/

# Serve regular files
# Remove www from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+)$ [NC]

# Check if there is a symlink for domain
RewriteCond /www/${lowercase:%2} -l

# Redirect and get files
RewriteRule ^/error/(.*)$ /www/error/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%2}htdocs,L]

### PHP handling

# Check if the requested file is a PHP file 
RewriteCond %{REQUEST_URI} \.(php|php3|php4|php5|phtml)

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+)$ [NC]

# Check if there is a symlink for domain
RewriteCond /www/${lowercase:%2} -l

# Do the redirect
RewriteRule ^/(.*)$ /www/${lowercase:%2}/htdocs/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%2}/htdocs,T=application/x-httpd-php,L]

# otherwise

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+)$ [NC]

# Check if there is a symlink for domain in /www
RewriteCond /www/${lowercase:%2} -l

# Do the redirect
RewriteRule ^/(.*)$ /www/${lowercase:%2}/htdocs/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%2}/htdocs,L]

####### SUBDOMAINS ########

# Deal with CGIs -- force a MIME type
RewriteCond %{REQUEST_URI} ^/cgi-bin/

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+?)\.(.+)$ [NC]

# Check if subdomain folder exists
RewriteCond /www/${lowercase:%3}/subdomener/${lowercase:%2} -d

# Do the redirect
RewriteRule ^/(.*)$ /www/${lowercase:%3}/subdomener/${lowercase:%2}/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%3}/subdomener/${lowercase:%2},T=application/x-httpd-cgi,L]

# Handle errors
RewriteCond %{REQUEST_URI} ^/error/
RewriteCond %{HTTP_HOST} ^(www\.)?(.+?)\.(.+)$ [NC]
RewriteCond /www/${lowercase:%3}/subdomener/${lowercase:%2} -d
RewriteRule ^/error/(.*)$ /www/error/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%3}/subdomener/${lowercase:%2},L]

### PHP, again

# Check if the requested file is a PHP file (note the .pthml missing - gotta love consistency. Should be cleaned up further.)
RewriteCond %{REQUEST_URI} \.(php|php3|php4|php5)

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+?)\.(.+)$ [NC]

# Check if subdomain folder exists
RewriteCond /www/${lowercase:%3}/subdomener/${lowercase:%2} -d

# Do the redirect
RewriteRule ^/(.*)$ /www/${lowercase:%3}/subdomener/${lowercase:%2}/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%3}/subdomener/${lowercase:%2},T=application/x-httpd-php,L]

# Remove www. from request
RewriteCond %{HTTP_HOST} ^(www\.)?(.+?)\.(.+)$ [NC]

# Check if subdomain folder exists under the main domain name - note: we do NOT
# make symlinks for subdomains, they just work automagically!
RewriteCond /www/${lowercase:%3}/subdomener/${lowercase:%2} -d

# Do the redirect
RewriteRule ^/(.*)$ /www/${lowercase:%3}/subdomener/${lowercase:%2}/$1 [E=DOCUMENT_ROOT:/www/${lowercase:%3}/subdomener/${lowercase:%2},L]

# If nothing else matches, redirect to a default page that has an explanation. Use with care.
#RewriteRule ^.*$ https://example.net/error/feil.html [R,L]

</Virtualhost>

Under /www må du opprette symbolske lenker til der webfilene faktisk ligger. Hvor de ligger er uvesentlig så lenge katalogstrukturen under domenenavnet er som følger:

/home/user/www/example.com/htdocs
/home/user/www/example.com/subdomener

For å opprette subdomener, bruk følgende struktur:

/home/user/www/example.com/subdomener/ting
/home/user/www/example.com/subdomener/jubel

Disse vil da nås i en nettleser som følger:
http(s)://example.com
http(s)://ting.example.com
http(s)://jubel.example.com

Merk at du linker til domene-mappen, ikke htdocs-mappen. Eksempel:

# cd /www
# ln -s /home/user/www/example.com example.com

Om du ønsker å bruke andre mappenavn enn htdocs og subdomener kan du endre dem til hva du vil så lenge det oppdateres i configen.