How to create a virtual host in Apache

How to create a virtual host in Apache

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 Host header 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.
  • sudo access.
  • A domain name (or you can test locally using your /etc/hosts file, 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 www subdomain) 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 a2ensite after creating the configuration file — the file existing in sites-available alone does nothing.
  • Conflicting ServerName values 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 ServerName matching 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 granted deliberately, understanding it grants public access to that directory; be more restrictive for admin areas.
  • Keep AllowOverride All limited to directories that genuinely need .htaccess support, since scanning for .htaccess files 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_deflate to 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.

References

Total
1
Shares

Leave a Reply

Previous Post
How to create a basic HTML page on Apache

How to Create a Basic HTML Page on Apache

Next Post
How to edit the Apache configuration file

How to Edit the Apache Configuration File

Related Posts