The ultimate guide on Virtual Hosts: how to host multiple apps on one server
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!
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.)
- (Debian-based only) After creating the file you should issue the following command:
- 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:
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!
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.)
- (Debian-based only) After creating the file you should issue the following command:
- 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:
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; } }
- 2020 A year in review for Marksei.com - 30 December 2020
- Red Hat pulls the kill switch on CentOS - 16 December 2020
- OpenZFS 2.0 released: unified ZFS for Linux and BSD - 9 December 2020
This is elegantly written. Thank you