The ultimate guide on Virtual Hosts: how to host multiple apps on one server

A bunch of mail boxes over a metal fence

Virtual hosting is a simple mechanism used by webservers to host multiple websites/web applications using the same server. The main advantage in using Virtual Hosts is that you reduce the number of servers and IP addresses needed to host your services and you can even serve multiple services using the same port.

Types of Virtual Hosts

Before diving into how to let’s take a look at the different types of Virtual Hosts (bear with me just a few seconds!):

  • Name-based: this is the one you’re probably after, you want to host multiple sites using the same ip:port. This virtual hosting method uses names to decide where the traffic will be directed.
  • IP-based: this one is actually pretty common and not known as a virtual hosting method. In this scenario a server is bound to multiple IP addresses and uses the IP to decide where the traffic should go. For example site1 is bound to IP1 and site2 is bound to IP2.
  • Port-based: this is actually an extension of the previous one and often used together. In this case the port is used to decide where the traffic will go. You can have site1 on ip1:port1 site2 on ip1:port2 and site3 on ip2:port3.

As you might imagine, the Name-based is the most popular since it lets you use the same ip:port combination to serve multiple services. Also you need to specify the port in web browsers when it isn’t 80 or 443, imagine having to tell your users to visit your.domain.tld:9078, it’d be a nightmare!

Important
I take NO responsibility of what you do with your machine; use this tutorial as a guide and remember you can possibly cause data loss if you touch things carelessly.
Tip!
Remember that www.domain.tld and domain.tld are two distinct entities! If you switch from one to the other on an existing site all your site (SE) may suffer!

Virtual Hosts using Apache

Virtual Hosts in Apache are quite straightforward. You just need to create a file inside one of Apache’s configuration directories. You can call these files however you want, but keep in mind they should end with .conf or they won’t be read!

  • For RedHat-based distributions: create a file in /etc/httpd/conf.d/SITENAME.conf
  • For Debian-based distributions: create a file in /etc/apache2/sites-available/SITENAME.conf
    • (Debian-based only) After creating the file you should issue the following command: # a2ensite SITENAME (this time the SITENAME should be the exact name excluding the .conf extension.)
  • For other distributions: follow the RedHat-based instructions as they use upstream configuration.

Let’s now take a look at a sample Virtual Host:

Listen 80
<VirtualHost *:80>
  ServerName YOURDOMAIN.TLD
  ServerAlias YOURALIAS.TLD
  ServerAdmin [email protected]
  DocumentRoot ROOTDIRECTORY

  ErrorLog ${APACHE_LOG_DIR}/yourdomain-error.log
  CustomLog ${APACHE_LOG_DIR}/yourdomain-access.log combined
</VirtualHost>

That’s it! Actually for a Virtual Host to work you just need three things:

  • The IP/port,
  • The ServerName,
  • The DocumentRoot

Now let’s take a look at the block line by line:

  • In Line 1 you tell the server to listen on port 80 (change the port to use a different one). Most of the times Apache will be already listening on port 80, you can safely remove this line unless you’re using a different port.
  • In Line 2 you define the ip:port. The * stands for “every IP the server can be bound to”. Most of the times you will want to use the asterisk, in other situations where you have multiple networks you may want to restrict the IP addresses your server is bound to.
  • Line 3 is the most important one, the ServerName directive is the one used to determine where the traffic will go!
  • If you have multiple names and you want to use them you can use ServerAlias.
  • ServerAdmin is a directive to specify the administrator email address. This email is mainly used in error pages and when sending emails.
  • The DocumentRoot directive tells the server to use the content within the directory to respond requests, it is important you point the server to where your files are! Usually under /var/www/html/something.
  • The last two lines within the block are optional, by adding them you can specify where Apache will store the error and access log for that virtual host only. If you’re using a Debian-based distribution you may want to change httpd with apache.

Once you’re happy with your configuration simply restart your server:

Redhat-basedDebian-based
# systemctl restart httpd
# systemctl restart apache2

SSL VHost using Apache

A Virtual Host won’t automatically serve SSL requests, that’s why you need another Virtual Host! You can place it in the same file you created. Here’s an example:

Listen 443
<VirtualHost *:443>
  ServerName YOURDOMAIN.TLD
  ServerAlias YOURALIAS.TLD
  ServerAdmin [email protected]
  DocumentRoot ROOTDIRECTORY

  SSLEngine on
  SSLCertificateFile /path/to/cert.pem
  SSLCertificateKeyFile /path/to/key.pem

  ErrorLog ${APACHE_LOG_DIR}/yourdomain-ssl-error.log
  CustomLog ${APACHE_LOG_DIR}/yourdomain-ssl-access.log combined
</VirtualHost>

Redirecting HTTP traffic to HTTPS

Another common task is to redirect traffic from the HTTP vhost to HTTPS, once again you can use two vhosts. You need mod_rewrite to make it work! The following example will also redirect all traffic to what your ServerName is, remember www vs non-www for SEO!

Redhat-basedDebian-based

No additional steps should be needed, but in case it doesn’t work, check one of the files inside /etc/httpd/conf.modules.d/ contains the following line. If no file contains the line you can add it to 00-base.conf.

LoadModule rewrite_module modules/mod_rewrite.so

Use the following commands:

# a2enmod rewrite
# systemctl restart apache2
Listen 80
Listen 443

<VirtualHost *:80>
  ServerName YOURDOMAIN.TLD
  ServerAlias YOURALIAS.TLD
  ServerAdmin [email protected]
  DocumentRoot ROOTDIRECTORY

  RewriteEngine ON
  RewriteCond %{SERVER_NAME} =YOURDOMAIN.TLD [OR]
  RewriteCond %{SERVER_NAME} =www.YOURDOMAIN.TLD
  RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

  ErrorLog ${APACHE_LOG_DIR}/yourdomain-error.log
  CustomLog ${APACHE_LOG_DIR}/yourdomain-access.log combined
</VirtualHost>

<VirtualHost *:443>
  ServerName YOURDOMAIN.TLD
  ServerAlias YOURALIAS.TLD
  ServerAdmin [email protected]
  DocumentRoot ROOTDIRECTORY

  SSLEngine on
  SSLCertificateFile /path/to/cert.pem
  SSLCertificateKeyFile /path/to/key.pem

  ErrorLog ${APACHE_LOG_DIR}/yourdomain-ssl-error.log
  CustomLog ${APACHE_LOG_DIR}/yourdomain-ssl-access.log combined
</VirtualHost>

Virtual Hosts (Server blocks) using Nginx

Virtual Hosts in nginx are more often referred as Server Blocks, while Nginx syntax seems harder compared to Apache, the amount of configuration is actually similar! In order to create a server block you just need to create a file inside Nginx configuration directories. You can call these files however you want, but keep in mind they should end with .conf or they won’t be read!

  • For RedHat-based distributions: create a file in /etc/nginx/conf.d/SITENAME.conf
  • For Debian-based distributions: create a file in /etc/nginx/sites-available/SITENAME.conf
    • (Debian-based only) After creating the file you should issue the following command: # ln -s /etc/nginx/sites-available/SITENAME.conf /etc/nginx/sites-enabled/SITENAME.conf(this time the SITENAME should be the exact name excluding the .conf extension.)
  • For other distributions: follow the RedHat-based instructions as they use upstream configuration.

Let’s now take a look at a server block:

server {
  listen  80;
  server_name SERVER_NAME SERVER_ALIAS;

  location / {
    root  ROOTDIRECTORY;
    index  index.html index.htm;
    try_files $uri $uri/ =404;
  }

  error_page  500 502 503 504  /50x.html;
  location = /50x.html {
      root  /usr/share/nginx/html;
  }
}

That’s it! Actually for a server block to work you just need three things:

  • The IP/port,
  • The server_name,
  • The root (directory)

Now let’s take a look at the code line by line:

  • In the first line in the block you can specify the IP/port. If you don’t specify the IP (as in this case) nginx will bind to every address it can bind to. If you want to specify an address you may do so using the syntax ip:port. Most of the times you will want to listen on multiple addresses, in other situations where you have multiple networks you may want to restrict the IP addresses your server is bound to.
  • In the second line you can specify a server name. This one is the most important since it will decide where your traffic will go. You may also specify a number of server aliases if you want to serve your content under different domain names.
  • The location block contains the root directory. You may specify a number of location blocks but you will need at least one with a / (root of the website) for it to work. The root tells the server where to find the content you want to serve.
  • The location block also contains a index directive that decides what happens when you visit the directory without specifying a file.
  • Inside the location block you can also specify a try_files directive in order to control what happens when there is no resource matching the request (a so-called “404“).
  • The last few lines are useful to specify what happens when a server error occurs, they are optional.

Once you’re happy with your configuration simply restart your server:

Redhat-basedDebian-based
# systemctl restart nginx
# systemctl restart nginx

SSL Server Block using Nginx

A Server Block won’t automatically serve SSL requests, that’s why you need another server blocks! You can place it in the same file you created. The following example will also redirect all traffic to what your ServerName is, remember www vs non-www for SEO! Here’s the example:

server {
  listen  443 ssl;
  server_name SERVER_NAME SERVER_ALIAS;

  ssl_certificate /path/to/cert.pem;
  ssl_certificate_key /path/to/key.pem;

  location / {
    root  ROOTDIRECTORY;
    index  index.html index.htm;
    try_files $uri $uri/ =404;
  }

  error_page  500 502 503 504  /50x.html;
  location = /50x.html {
      root  /usr/share/nginx/html;
  }
}

Redirecting HTTP traffic to HTTPS

Another common task is to redirect traffic from the HTTP server block to HTTPS, once again you can use two server blocks.

server {
  listen  80;
  server_name SERVER_NAME SERVER_ALIAS;

  return 301 https://$host$request_uri;
}

server {
  listen  443 ssl;
  server_name SERVER_NAME SERVER_ALIAS;

  ssl_certificate /path/to/cert.pem;
  ssl_certificate_key /path/to/key.pem;

  location / {
    root  ROOTDIRECTORY;
    index  index.html index.htm;
    try_files $uri $uri/ =404;
  }

  error_page  500 502 503 504  /50x.html;
  location = /50x.html {
      root  /usr/share/nginx/html;
  }
}
Image courtesy of ninita_7
mark

You may also like...

1 Response

  1. Albert says:

    This is elegantly written. Thank you

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: