One of the most useful features I rely on constantly when managing web servers is Apache’s virtual host system. It’s what allows me to host multiple websites, each with its own domain and document root, all on a single Apache server. In this guide, I’ll walk through exactly how I set up a virtual host from scratch, using a real, practical example.
What Is a Virtual Host?
A virtual host in Apache is essentially a set of configuration directives that tell the server how to handle requests for a specific domain or IP address. Instead of running a separate Apache instance for every site, I can run one Apache installation that serves multiple websites, each isolated in its own configuration block.
There are two main types of virtual hosts:
- Name-based virtual hosts — multiple domains share the same IP address, and Apache differentiates between them using the
Hostheader sent by the browser. This is by far the most common setup. - IP-based virtual hosts — each site has its own unique IP address. This is less common today given the availability of name-based hosting.
I’ll focus on name-based virtual hosts in this guide, since that’s what the vast majority of real-world setups use.
Prerequisites
- Apache installed and running on Ubuntu.
sudoaccess.- A domain name (or you can test locally using your
/etc/hostsfile, which I’ll show below).
Step 1: Create the Directory Structure
I like to organize each site under /var/www/, using the domain name as the folder name for clarity:
sudo mkdir -p /var/www/example.com/html
Step 2: Assign Ownership and Permissions
sudo chown -R $USER:$USER /var/www/example.com/html
sudo chmod -R 755 /var/www/example.com
Step 3: Create a Sample Page
To have something to actually test with, I create a simple index.html file:
nano /var/www/example.com/html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Welcome to example.com</title>
</head>
<body>
<h1>Success! The virtual host for example.com is working.</h1>
</body>
</html>
Step 4: Create a New Virtual Host Configuration File
Apache virtual host configuration files live in /etc/apache2/sites-available/. I create one specifically for this site rather than editing the default:
sudo nano /etc/apache2/sites-available/example.com.conf
I add the following configuration:
<VirtualHost *:80>
ServerAdmin admin@example.com
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/html
<Directory /var/www/example.com/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/example.com_error.log
CustomLog ${APACHE_LOG_DIR}/example.com_access.log combined
</VirtualHost>
Here’s what each directive does, since I always want to understand exactly what I’m configuring:
- ServerName — the primary domain this virtual host responds to.
- ServerAlias — additional domain variations (like the
wwwsubdomain) that should also match this block. - DocumentRoot — the directory Apache serves files from for this specific site.
- Directory block — controls access and override behavior for that specific folder.
- ErrorLog / CustomLog — dedicated log files for this site, which makes troubleshooting much easier than digging through a shared log.
Step 5: Enable the New Virtual Host
sudo a2ensite example.com.conf
This creates a symlink in /etc/apache2/sites-enabled/, activating the configuration.
Step 6: Disable the Default Site (Optional)
If I don’t want the default Apache page competing with my new virtual host, I disable it:
sudo a2dissite 000-default.conf
Step 7: Test the Configuration
Before reloading Apache, I always validate the syntax:
sudo apachectl configtest
If everything checks out, I reload Apache:
sudo systemctl reload apache2
Step 8: Test Locally (If You Don’t Have DNS Set Up Yet)
If I’m testing this on a server without a real domain pointed to it yet, I add an entry to my local machine’s hosts file so I can test by name.
On Linux/macOS, I edit:
sudo nano /etc/hosts
And add:
your_server_ip example.com www.example.com
On Windows, the equivalent file is located at C:\Windows\System32\drivers\etc\hosts.
After saving, I visit http://example.com in my browser, and it should resolve to my server and display the custom page I created.
Setting Up Multiple Virtual Hosts
The real power of virtual hosts comes from hosting multiple sites on one server. I simply repeat the process for each additional domain:
sudo mkdir -p /var/www/anotherdomain.com/html
sudo nano /etc/apache2/sites-available/anotherdomain.com.conf
<VirtualHost *:80>
ServerAdmin admin@anotherdomain.com
ServerName anotherdomain.com
ServerAlias www.anotherdomain.com
DocumentRoot /var/www/anotherdomain.com/html
<Directory /var/www/anotherdomain.com/html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/anotherdomain.com_error.log
CustomLog ${APACHE_LOG_DIR}/anotherdomain.com_access.log combined
</VirtualHost>
sudo a2ensite anotherdomain.com.conf
sudo apachectl configtest
sudo systemctl reload apache2
Apache automatically routes incoming requests to the correct virtual host based on the Host header in the request, matching it against the ServerName and ServerAlias directives.
Adding HTTPS to a Virtual Host
Once a virtual host is working over HTTP, I usually add SSL using Certbot, which automatically creates a second <VirtualHost *:443> block and configures the certificate paths:
sudo apt install certbot python3-certbot-apache -y
sudo certbot --apache -d example.com -d www.example.com
Certbot handles most of the heavy lifting here, including setting up automatic HTTP-to-HTTPS redirects if I choose that option during the prompt.
Common Mistakes When Creating Virtual Hosts
- Forgetting to enable the site with
a2ensiteafter creating the configuration file — the file existing insites-availablealone does nothing. - Conflicting
ServerNamevalues across multiple virtual host files, which confuses Apache about which site should handle a given request. - Not testing configuration before reloading, risking downtime from a simple typo.
- Leaving the default site enabled when it’s no longer needed, which can sometimes catch requests unintentionally if
ServerNamematching isn’t precise. - Incorrect file permissions on the new document root, leading to a “403 Forbidden” error even though the virtual host configuration itself is correct.
Security Best Practices
- Use
Require all granteddeliberately, understanding it grants public access to that directory; be more restrictive for admin areas. - Keep
AllowOverride Alllimited to directories that genuinely need.htaccesssupport, since scanning for.htaccessfiles on every request adds a small performance cost. - Set up dedicated log files per virtual host, as I did above, to make auditing and troubleshooting far easier.
- Always add SSL certificates for any publicly accessible virtual host, especially if any form of user data is being submitted.
Performance Optimization Tips
- Group similar static content under one virtual host with proper caching headers (
mod_expires) rather than fragmenting it unnecessarily across multiple domains. - Monitor per-site access logs to identify unusually high traffic hitting a specific virtual host, which can help with capacity planning.
- Use
mod_deflateto compress responses for each virtual host, applied globally in the main configuration so all sites benefit.
Troubleshooting Common Issues
My virtual host isn’t loading; the default page still shows: Double-check that the site is enabled (a2ensite) and that the default site isn’t taking priority (a2dissite 000-default.conf if needed), then reload Apache.
Getting a “403 Forbidden” error: Check ownership and permissions on the virtual host’s document root:
sudo chown -R www-data:www-data /var/www/example.com/html
sudo chmod -R 755 /var/www/example.com
Apache won’t reload after adding a new virtual host: Run sudo apachectl configtest to find the exact syntax error.
Frequently Asked Questions
How many virtual hosts can I run on one Apache server? There’s no hard limit imposed by Apache itself; the practical limit depends on your server’s resources and traffic volume.
Do I need a different IP address for each virtual host? No, name-based virtual hosting allows multiple domains to share a single IP address.
What’s the difference between ServerName and ServerAlias? ServerName is the primary domain for the virtual host, while ServerAlias lets you specify additional domain names (like a www variant) that should also match the same block.
Can I use virtual hosts for subdomains too? Yes, subdomains work exactly the same way; just set the ServerName to the specific subdomain, like blog.example.com.
Summary and Key Takeaways
Creating a virtual host in Apache involves setting up a dedicated document root, writing a configuration file under sites-available, enabling it with a2ensite, and reloading Apache after validating the syntax. This system is what makes it possible to host multiple independent websites on a single server efficiently, and once you’ve done it a few times, it becomes second nature.