Apache Virtual Hosts
A single Apache server can host many websites at once. The feature that makes this possible is called a virtual host (often shortened to vhost) — a block of configuration that tells Apache “when a request comes in for this domain name, serve files from this folder.” Without virtual hosts you would need one server per site, which is slow and expensive. In this page you will create a real virtual host on Ubuntu, enable it, turn off the default site, and reload Apache so your new site goes live.
This guide is the Apache equivalent of the Nginx “server blocks” page. The idea is identical; only the file layout and commands differ.
How Apache organises sites on Ubuntu
On Ubuntu (22.04 / 24.04 LTS), the Apache package (apache2) ships with a tidy two-folder system so you can keep many site configs around but only switch on the ones you want.
| Folder | What it holds |
|---|---|
/etc/apache2/sites-available/ | All site config files (.conf), enabled or not. This is where you write them. |
/etc/apache2/sites-enabled/ | Symbolic links (shortcuts) pointing back to files in sites-available. Apache only reads these. |
When to use this layout: always. Keeping every config in
sites-availableand only linking the active ones intosites-enabledmeans you can disable a site instantly without deleting its config. Never edit files directly insidesites-enabled— edit the real file insites-available, because the link just mirrors it.
You never create the links by hand. Two helper commands do it for you:
a2ensite— apache2 enable site (creates the link).a2dissite— apache2 disable site (removes the link).
Step 1: Create the document root and a test page
The document root is the folder on disk where your website’s files live. Let’s host a site for example.test and give it its own folder.
sudo mkdir -p /var/www/example.test/public_html
sudo chown -R $USER:$USER /var/www/example.test/public_html
echo "<h1>Hello from example.test</h1>" | sudo tee /var/www/example.test/public_html/index.html
The chown line (change owner) hands the folder to your user so you don’t need sudo for every edit. $USER is automatically your login name.
Step 2: Write the virtual host config
Create a .conf file in sites-available. The filename should match the domain so it’s easy to find later.
sudo nano /etc/apache2/sites-available/example.test.conf
Paste this configuration:
<VirtualHost *:80>
ServerName example.test
ServerAlias www.example.test
DocumentRoot /var/www/example.test/public_html
<Directory /var/www/example.test/public_html>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/example.test-error.log
CustomLog ${APACHE_LOG_DIR}/example.test-access.log combined
</VirtualHost>
What each line does:
<VirtualHost *:80>— this block handles requests arriving on port 80 (plain HTTP) for any IP address (*). Apache decides which block to use by matchingServerName.ServerName— the main domain this site answers to.ServerAlias— extra names that also map here (sowww.example.testworks too).DocumentRoot— the folder Apache serves files from.<Directory>— access rules for that folder.Require all grantedallows the public to read it;AllowOverride Alllets.htaccessfiles override settings;Options -Indexeshides the file listing when there’s noindex.html(a small security win).ErrorLog/CustomLog— per-site log files.${APACHE_LOG_DIR}expands to/var/log/apache2.
Step 3: Enable the site and disable the default
Ubuntu ships with a default site (000-default.conf) that catches every request. Turn it off so your new site responds first, then enable your site.
sudo a2ensite example.test.conf
sudo a2dissite 000-default.conf
Output:
Enabling site example.test.
To activate the new configuration, you need to run:
systemctl reload apache2
Site 000-default disabled.
To activate the new configuration, you need to run:
systemctl reload apache2
These commands only create and remove the symlinks in sites-enabled. Nothing is live until you reload Apache.
Step 4: Test the config and reload
Always check your syntax before reloading — a typo can take every site on the box down.
sudo apache2ctl configtest
Output:
Syntax OK
If you see Syntax OK, apply the change:
sudo systemctl reload apache2
reload re-reads the config without dropping live connections, unlike restart which fully stops and starts the service.
Step 5: Verify it works
If you don’t own the domain yet, fake the DNS lookup by adding a line to your local /etc/hosts file so example.test points at the server.
echo "127.0.0.1 example.test" | sudo tee -a /etc/hosts
curl -H "Host: example.test" http://127.0.0.1
Output:
<h1>Hello from example.test</h1>
The -H "Host: example.test" header tells Apache which virtual host you want, exactly as a browser would. To repeat this for a second site, copy the four steps with a new folder, domain, and .conf file.
a2ensite vs editing sites-enabled directly
| Approach | When to use | Risk |
|---|---|---|
a2ensite / a2dissite | Always — the supported way | None; it’s reversible |
| Manually creating symlinks | Scripted/automated provisioning only | Easy to typo the path |
Editing files in sites-enabled | Never | You’re editing a link target; confusing and easy to lose work |
Gotcha: the order Apache evaluates virtual hosts matters. If no
ServerNamematches the incoming request, Apache falls back to the first enabled vhost (alphabetically by filename). That’s why Ubuntu prefixes its default with000-— keep that in mind when a request lands on the wrong site.
Best Practices
- Name each config file after its domain (
example.test.conf) sosites-availablestays readable. - Always run
sudo apache2ctl configtestbefore reloading — one bad block breaks all sites. - Give every vhost its own
ErrorLogandCustomLogso you can debug one site without noise from others. - Disable
000-default.confin production so unmatched requests don’t leak the default Ubuntu page. - Use
reload, notrestart, for config changes to avoid dropping active connections. - Add HTTPS later with a separate
<VirtualHost *:443>block (Certbot can generate it for you).