Everyone has their reasons as to why they want to set up name-based virtual hosts rather than IP-based virtual hosts on Apache.
For myself, I produce many small websites as part of my business as well as for my personal projects. It does not make sense to deploy an instance for each and every website, especially if many of the websites are small and low in traffic.
One way to save on costs is to use a shared hosting provider that gives an “unlimited” website and resources plan.
However, at times, I would rather not have my website on a shared hosting plan, or I may need something faster and more powerful. Or perhaps, I’m not familiar with who the reputable hosting companies are in certain regions where I’m planning to host my website at. (Shared hosting is not made equal; there great ones, and there are outright horrible ones)
This is where I will launch a cheap instance from AWS, Linode, Google Cloud or any other cloud or VPS hosting providers out there. These instances are really cheap yet powerful, giving you incredible value for your dollar. Many of them have data centers located all around the world, they let you choose the resources that you require, and I know that I cannot go wrong with any of them.
However, costs can also stack up if I deploy one instance for every website, especially if many of the websites are under-utilising the allocated resources.
Here is where knowing how to set up name-based virtual hosts comes in handy.
Why do I say so? Using name-based virtual hosts, you can then map multiple domains onto one IP address. This means that you can deploy a single instance with just 1 IP address and run multiple websites on that instance, will tremendously cut your running costs especially if you have many small websites!
There are some prerequisites to applying this guide.
1) You must be running Apache (such as a LAMP stack)
2) You need to be able to access and modify your Apache config
3) You need to have basic server administration knowledge
4) You need to have the urge to save lots of money 😄
Assuming your setup is running as an IP-based virtual host, the change to name-based virtual host is really easy once you understand how it works.
In this guide, my objective will be to host 2 WordPress sites (or could be anything else) using Apache's virtual hosts. The idea is to have 2 websites to load from the same IP address, so that I can essentially share the instance and resources between both sites. I will also be touching on enabling SSL on your sites with Certbot, and some basic redirection rules to get your sites automatically pointing to the HTTPS version.
The 2 websites I will be configuring will be virtual-host.xyz and virtual-host.icu. Don't bother checking out these sites as I only purchased the domains for the purposes of this demonstration.
So for this guide, I have launched an instance on Linode to show you the process.
Let's create an once-click instance and select LAMP. With this, all the required packages to run a WordPress site will be installed.
(Note that you can also choose a one-click WordPress installation, but I prefer starting off with a LAMP stack if I am working with multiple sites, as the configuration files are cleaner on the get go.)
To ensure the DNS propagates as soon as possible, we shall first set up an A record to point your domains to the instance’s IP address. I will be using Linode's DNS to do so:
Once this is done, we shall remote login to our server’s network via SSH. Open up terminal and connect to your instance via SSH: (Substitute your values accordingly)
ssh [email protected]
As with every new instance, we should make it a good habit to update and upgrade our packages .
sudo apt update && sudo apt upgrade
Before we configure our apache config files, we first need to create the separate directories which will contain our websites. The directories will then be specified in the relevant Apache config files later on.
For the site virtual-host.xyz:
sudo mkdir -p /var/www/virtual-host.xyz/public_html
And for the site virtual-host.icu:
sudo mkdir -p /var/www/virtual-host.icu/public_html
Just to make sure both of my new directories' permissions are set to 755:
sudo chmod 755 /var/www/virtual-host.xyz/public_html /var/www/virtual-host.icu/public_html
Now since I have already created the file directories, I might as well create the databases first.
sudo mariadb
create database virtual_1;
# Substitute virtual_1 to however you want to name your database
create user 'user_1' identified by 'password_1';
# Substitute user_1 and password_1 to however you want accordingly
grant all privileges on virtual_1.* to 'user_1';
# Same thing, substitute virtual_1 and user_1 accordingly
flush privileges;
quit
Next, let's go over to the folder that contains your Apache config file:
cd /etc/apache2/sites-available
ls
We should be able to see 2 files, 000-default.conf and default-ssl.conf. We will not be touching default-ssl.conf at all (Certbot will help us configure for SSL later on), so let's just make sure it's disabled:
sudo a2dissite default-ssl.conf
Next we shall take a look at the initial Apache config file that was provided to us:
sudo nano /etc/apache2/sites-available/000-default.conf
Base on the configuration above, you are running on an IP-based virtual host. To change it to a name-based virtual host, here are the steps.
Edit the 000-default.conf file accordingly, or to make things easier, wipe everything and input the following:
<VirtualHost *:80>
ServerName virtualhost.xyz
ServerAlias www.virtualhost.xyz
ServerAdmin webmaster@localhost
DocumentRoot /var/www/virtualhost.xyz/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
(Be sure to change virtualhost.xyz under ServerName, ServerAlias and DocumentRoot to your own domain name. ServerAdmin, ErrorLog and CustomLog will remain exactly as it is.)
For your second site, copy the same thing again and add it right below, on the same file. The entire file should now look like this:
<VirtualHost *:80>
ServerName virtual-host.xyz
ServerAlias virtual-host.xyz
ServerAdmin webmaster@localhost
DocumentRoot /var/www/virtual-host.xyz/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:80>
ServerName virtual-host.icu
ServerAlias virtual-host.icu
ServerAdmin webmaster@localhost
DocumentRoot /var/www/virtual-host.icu/public_html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Then reload Apache:
sudo systemctl reload apache2
So what this does is really simple. Each time a name-based http request comes in (or to put it simply, a visitor types in your domain and visits your site), Apache will point it to the relevant document directory according to how the config file is set. ServerName specifies the domain name, and DocumentRoot specifies where the files are located at.
Do note that this is for port 80, which is for http traffic. If you are intending to serve your website on https, we then need to set up the right redirections and create name-based virtual hosts for port 443 as well.
Note: You might have encountered older guides teaching you to declare the NameVirtualHost directive. If you are running in an updated setup, you do NOT need to explicitly declare this directive anymore. In fact, if you do so at this time of writing, you will receive a notification informing you on the upcoming deprecation:
NameVirtualHost has no effect and will be removed in the next release
The simplest way to do this is to add an index.html file to both directories. (Substitute your values accordingly)
sudo nano /var/www/virtual-host.xyz/public_html
This is virtual-host.xyz!
sudo nano /var/www/virtual-host.icu/public_html
This is virtual-host.icu!
Then we can load both sides and check if they are displaying the correct files.
Remember to delete the index.html files once you have confirmed everything is working: (Substitute your values accordingly)
sudo rm /var/www/virtual-host.xyz/public_html/index.html
sudo rm /var/www/virtual-host.icu/public_html/index.html
Now what's the point of configuring for multiple websites if they are not SSL-enabled? In today's expectations, all websites need to be SSL-enabled, especially when Let's Encrypt provides free certificates. When I come across websites that are still not running on HTTPS, credibility points get knocked off right away. Also, according to Google themselves, having your site on HTTPS has been a ranking signal for many years, albeit a lightweight one.
Fortunately, Certbot makes the installation and renewal of Let's Encrypt SSL certificates a breeze. We need to first install Certbot:
sudo apt install certbot python-certbot-apache
Once we are sure that our DNS has propagated and is pointing to our instance, we will then run Certbot on Apache:
sudo certbot --apache
You will be asked to fill in an email address, agree to the terms of service, opt for marketing email and choose the site that you want to install the certificate for.
Now this part becomes a little tricky to proceed. You will be given a list of domain names and their respective www versions. You should only be installing the certificate for one domain at a time. Activating either one at a time or all at one go will still result in only 1 certificate being created, which means that all your websites will be using 1 common name for the certificate. Here is how a mismatched certificate looks like:
So to create matching certificates, you need run Certbot again for the next domain name and so on.
For this example though, I will activate all certificates in one go, and show you how to make adjustments again when we want to create unique certificates for each domain.
So back to where we were, I've selected both sites, option 1 and 2. I did not select 3 and 4 as I do not intend to run my site with the www subdomain.
Then choose the option to redirect http traffic to https: (This step adds redirection rules to our 000-default.conf file)
Once Certbot has completed the SSL certification creation, we are ready to double check our Apache config files. Note that a new file 000-default-le-ssl.conf has been added, and modifications have also been made to our existing 000-default.conf file.
sudo nano /etc/apache2/sites-available/000-default.conf
Confirm that the following redirection rule has been added to the file for all your virtual hosts:
RewriteEngine on
RewriteCond %{SERVER_NAME} =virtual-host.xyz [OR]
RewriteCond %{SERVER_NAME} =www.virtual-host.xyz
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
FYI: The above rule take http://virtual-host.xyz or http://www.virtual-host.xyz and redirects it to https://virtual-host.xyz.
Next, check that 000-default-le-ssl.conf has been configured correctly too:
Once confirmed, reload Apache and your virtual hosts have been successfully configured with SSL:
sudo systemctl reload apache2
In order for your certificate name to match your domain name, we have to run Certbot for each domain separately, according to what I mentioned previously.
Go ahead and run Certbot again for the domain you wish to add the certificate to. But this time round, do not select the option to redirect http to https as we have already configured that.
Once that is complete, located the folder containing all our SSL certificates:
cd /etc/letsencrypt/live/virtual-host.icu
You should find the files fullchain.pem and privkey.pem here. Note down the path and substitute it in our Apache SSL config file:
sudo nano /etc/apache2/sites-available/000-default-le-ssl.conf
Then finally, reload Apache and you are done:
sudo systemctl reload apache2
Your domain names should now be matching the respective certificate names.
Apache could not restart - This is a clear sign that your config file has errors in the syntax. Run the command: sudo apache2ctl configtest, and you will be pinpointed to the exact location of the error. Go back and fix it.
Forbidden 403 - This is usually caused by an incorrect virtual host setup. Go back and check every single line of the file to make sure you do not have any typos, such as missing a backslash or using curly double quotes.
The site address entered is loading the wrong website, or is caught in a loop - This is usually caused by incorrect redirection rules.
Typing in absolute URLs of my website into my browser causes 404 or 403 error - Again, very likely an error with your redirection rules. Map out all possible combinations, and find out what each one of them is doing by typing them one by one into your browser like this:
http://www.example.com
http://example.com
https://example.com
https://www.example.com
If everything is working fine. All of them should be redirecting you to only 1 final URL (important to keep this consistency especially for SEO).
I’m lost and have absolutely no idea what is going on - Perhaps, it’s better if you choose a managed approach, such as Cloudways. You can forget about the trouble of server administration and maintenance, and let Cloudways manage your server for you for just a few dollars more each month.
I hope this guide has been of help to you. Once you get a hang of how Apache configuration works, setting up name-based virtual hosts will eventually become easier. If you have further questions, feel free to leave them in the comments below, and I will try my best to help.