← Back to Bootcamp

Lesson 4: Virtual Hosts & Multi-Site Apache

Run multiple websites from one server using Apache virtual hosts

Prep
Apache
Files
Firewall
Network
Router
DNS
HTTPS
Progress: 0/4 steps completed
1
What Are Virtual Hosts?
One server, many websites

The Problem

By default, Apache serves one website from /var/www/html/. But you might want blog.yourdomain.com, shop.yourdomain.com, and yourdomain.com all on the same machine. Virtual hosts solve this.

What is a Virtual Host?

A virtual host is a configuration block that tells Apache: "When a request comes in for this domain, serve files from this folder using these settings." Apache can have dozens of virtual hosts, each isolated from the others.

Name-Based vs. IP-Based

Name-based virtual hosts (what we use) distinguish sites by the domain name in the HTTP request. IP-based uses different IP addresses. Name-based is standard because it requires only one IP.

2
Create Directory Structure
Separate folders for each site

Why separate folders?

Each virtual host needs its own DocumentRoot — the folder Apache looks in for that site's files. Keeping them separate prevents one site from accidentally accessing another's files.

Create folders for two example sites

sudo mkdir -p /var/www/blog sudo mkdir -p /var/www/shop

Create placeholder index files

echo "<h1>Blog</h1>" | sudo tee /var/www/blog/index.html echo "<h1>Shop</h1>" | sudo tee /var/www/shop/index.html

Set ownership

sudo chown -R www-data:www-data /var/www/blog /var/www/shop sudo chmod -R 755 /var/www/blog /var/www/shop
3
Configure Virtual Hosts
Tell Apache which domain serves which folder

Apache configuration files

Virtual host configs live in /etc/apache2/sites-available/. You create a file there, then enable it with a2ensite. Enabled sites are symlinked to /etc/apache2/sites-enabled/.

Create the blog virtual host

sudo nano /etc/apache2/sites-available/blog.conf

Paste this inside:

<VirtualHost *:80> ServerName blog.yourdomain.com DocumentRoot /var/www/blog <Directory /var/www/blog> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/blog-error.log CustomLog ${APACHE_LOG_DIR}/blog-access.log combined </VirtualHost>

Create the shop virtual host

sudo nano /etc/apache2/sites-available/shop.conf

Paste this inside (change the domain and path):

<VirtualHost *:80> ServerName shop.yourdomain.com DocumentRoot /var/www/shop <Directory /var/www/shop> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> ErrorLog ${APACHE_LOG_DIR}/shop-error.log CustomLog ${APACHE_LOG_DIR}/shop-access.log combined </VirtualHost>

Enable the sites

sudo a2ensite blog.conf sudo a2ensite shop.conf sudo a2dissite 000-default.conf
  • a2ensite creates a symlink from sites-available to sites-enabled
  • a2dissite 000-default disables the default placeholder site
  • Without disabling the default, Apache might serve it instead of your virtual hosts

Test and reload

sudo apache2ctl configtest sudo systemctl reload apache2
4
Test Multi-Site Routing
Verify each domain reaches the correct folder

How Apache decides which virtual host to use

When a browser sends a request, it includes the domain name in the Host header. Apache reads this header and matches it against the ServerName in each virtual host. The first match wins. If no match, the first virtual host alphabetically is used as the default.

Test with curl from your server

curl -H "Host: blog.yourdomain.com" http://localhost

You should see <h1>Blog</h1>

curl -H "Host: shop.yourdomain.com" http://localhost

You should see <h1>Shop</h1>

From the internet

Make sure DNS A records point each subdomain to your public IP. Then visit:

http://blog.yourdomain.com http://shop.yourdomain.com

⚠️ Wrong site showing?

  • Check ServerName — Must match the domain exactly, including subdomains.
  • Disable the defaultsudo a2dissite 000-default.conf is required.
  • Check DNS — Each subdomain needs its own A record pointing to your IP.
  • Order matters — Apache uses the first matching virtual host. Use apache2ctl -S to see the order.