Create and Enable a New Apache Virtual Host
Learn to configure, enable, test, and troubleshoot a name-based Apache virtual host on Debian or Ubuntu.
Apache HTTP Server is web server software that processes HTTP and HTTPS requests. A virtual host is an Apache configuration block that describes how one website or hostname should be served.
With name-based virtual hosting, one Apache server and one IP address can serve multiple websites. Apache examines the hostname in each request and selects the matching ServerName or ServerAlias. Each website should have its own hostname, document root, configuration file, and enabled virtual-host definition.
How Apache name-based virtual hosting works
A browser requesting http://example.test/ sends an HTTP Host header such as Host: example.test. Apache compares that hostname with the virtual hosts configured for the requested listener, normally port 80 for HTTP. When it finds a match, it serves files from that site's DocumentRoot.
For example, one server can provide:
example.testfrom/var/www/example.testother.testfrom/var/www/other.test
The sites can share an IP address because the hostname, rather than a separate IP address, identifies the intended site. A request made only to the IP address might not match either name and can therefore be handled by the default or first virtual host.
Apache site configuration layout
Debian and Ubuntu organize Apache site definitions into two directories:
/etc/apache2/sites-available/contains site configuration files that are available but not necessarily active. See Sites Available Directory./etc/apache2/sites-enabled/contains links to the site configurations that Apache should load. See Sites Enabled Directory.
The packaged default HTTP site is usually /etc/apache2/sites-available/000-default.conf. It is a useful starting template for a basic HTTP virtual host. The a2ensite command enables an available site by creating the appropriate link in sites-enabled. a2dissite removes that enabled link without deleting the available configuration.
Create the document root
The DocumentRoot is the filesystem directory from which Apache serves a site's public files. Give each site a dedicated directory so its content and permissions are easy to manage.
sudo mkdir -p /var/www/example.test
For a simple static site, root ownership with readable files is sufficient. Directories need execute permission so Apache can traverse them; files need read permission so Apache can serve them.
sudo chown -R root:root /var/www/example.test
sudo find /var/www/example.test -type d -exec chmod 755 {} \;
sudo find /var/www/example.test -type f -exec chmod 644 {} \;
For an application that must write uploads or cache data, do not make the entire document root writable. Create narrowly scoped writable directories and choose ownership according to the application's security requirements.
Create a test page
Add an index.html file with a distinctive title and message. This makes it clear that Apache served the new document root rather than the default page.
sudo tee /var/www/example.test/index.html > /dev/null <<'EOF'
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>example.test</title></head>
<body><h1>example.test is working</h1></body>
</html>
EOF
Create a separate site configuration
Keep each website in its own configuration file. Separate files make sites easier to enable, disable, review, troubleshoot, and later extend with TLS or application-specific settings.
Copy the default HTTP configuration to a descriptively named file in sites-available:
sudo cp /etc/apache2/sites-available/000-default.conf \
/etc/apache2/sites-available/example.test.conf
Open the new file with an editor, such as:
sudo nano /etc/apache2/sites-available/example.test.conf
Replace or adjust its contents so the listener and site directives look like this:
<VirtualHost *:80>
ServerAdmin webmaster@example.test
ServerName example.test
ServerAlias www.example.test
DocumentRoot /var/www/example.test
ErrorLog ${APACHE_LOG_DIR}/example.test-error.log
CustomLog ${APACHE_LOG_DIR}/example.test-access.log combined
</VirtualHost>
The <VirtualHost *:80> block handles HTTP requests received on port 80 on the server's interfaces. Keep the listener appropriate for HTTP; HTTPS normally uses a separate TLS virtual host on port 443.
Important virtual-host directives
| Directive | Purpose | Example value | Required or optional |
|---|---|---|---|
ServerAdmin | Administrative contact associated with the site configuration. | webmaster@example.test | Recommended |
ServerName | Primary fully qualified hostname Apache uses to match requests. | example.test | Required for predictable name matching |
ServerAlias | Additional hostnames served by the same virtual host. | www.example.test | Optional |
DocumentRoot | Directory containing the site's public files. | /var/www/example.test | Required |
ErrorLog | Destination for server-side errors from this virtual host. | ${APACHE_LOG_DIR}/example.test-error.log | Recommended |
CustomLog | Destination and format for requests handled by this virtual host. | ${APACHE_LOG_DIR}/example.test-access.log combined | Recommended |
Use a working administrative email address for ServerAdmin. Set ServerName to the hostname users will enter. Add a ServerAlias such as www.example.test only when that name should serve the same site. Separate access and error logs make it easier to identify requests and diagnose failures; see Apache access and error log files.
Make the hostname resolve to the server
The configured hostname must resolve to the server's IP address. For public access, create the appropriate DNS records. For a private test, add a hosts-file entry on the client running the browser or curl:
192.0.2.10 example.test www.example.test
Replace 192.0.2.10 with the server's actual address. A hosts-file entry affects only that client and is useful before public DNS is ready.
Browsing to the IP address alone does not reliably test a named virtual host. Without the configured hostname, Apache may select the default virtual host. You can isolate Apache's routing from DNS by sending an explicit Host header:
curl -i -H 'Host: example.test' http://127.0.0.1/
The HTTP Host header is the request header Apache uses for name-based virtual-host selection.
Enable and apply the site
Enable the available configuration with its filename:
sudo a2ensite example.test.conf
Always validate the complete Apache configuration before reloading the service:
sudo apache2ctl configtest
A successful check reports Syntax OK. Apply the configuration with a reload, which normally keeps existing connections alive:
sudo systemctl reload apache2
A restart is also possible, but it stops and starts the service and is generally unnecessary for a normal site-file change:
sudo systemctl restart apache2
If the default page should not answer unmatched hostnames, disable the packaged default site and reload:
sudo a2dissite 000-default.conf
sudo systemctl reload apache2
Do this only after confirming that the intended virtual hosts are configured. The default site can be useful as a fallback while testing.
Site-management command reference
| Command | Purpose | When to use it |
|---|---|---|
a2ensite example.test.conf | Enables an available site. | After creating or changing a site that should be active. |
a2dissite 000-default.conf | Disables an enabled site. | When a site should no longer answer requests. |
apache2ctl configtest | Checks Apache configuration syntax. | Before every reload or restart. |
systemctl reload apache2 | Loads configuration changes with minimal disruption. | After a successful syntax check. |
systemctl restart apache2 | Stops and starts Apache. | When a restart is specifically required. |
apache2ctl -S | Lists active virtual hosts, listeners, and name mappings. | When verifying routing or diagnosing the wrong site. |
Verify the virtual host
- Confirm that the hostname resolves to the expected server IP.
- Open
http://example.test/in a browser. - Confirm that the page says
example.test is working. - Run
sudo apache2ctl -Sand confirm that the new name appears under the expected listener. - Review
example.test-access.logandexample.test-error.logif the result is unexpected.
sudo apache2ctl -S
curl -i -H 'Host: example.test' http://127.0.0.1/
Troubleshooting
The default Apache page appears
- Check that
sudo a2ensite example.test.confsucceeded. - Run
sudo apache2ctl configtest, then reload Apache. - Make sure the browser hostname exactly matches
ServerNameorServerAlias. - Use the explicit
Hostheader with curl to separate Apache routing from DNS problems. - Run
sudo apache2ctl -Sto inspect enabled mappings. An unmatched request may be handled by the default site.
Apache will not reload or restart
- Run
sudo apache2ctl configtestand correct the reported file and line. - Check that directives are inside the
VirtualHostblock where appropriate. - Verify configuration filenames and referenced filesystem paths.
- Review service status and the Apache error log for additional details.
A 403 Forbidden response appears
- Confirm that Apache can traverse every parent directory of the document root.
- Check directory execute permissions and file read permissions.
- Verify that no applicable
Directoryrule denies access. - Review the virtual host's error log.
A 404 Not Found response appears
- Compare
DocumentRootwith the actual directory path. - Confirm that
/var/www/example.test/index.htmlexists and is readable. - Check that the requested URL corresponds to an existing file or application route.
- Review access and error logs for the requested path.
The domain fails from another machine
- Check that DNS returns the server's correct IP address.
- Remember that DNS changes can take time to propagate.
- Verify that a firewall or network security rule allows TCP port 80.
- Check that Apache is listening on the expected interface and port.
Production considerations
Once the HTTP virtual host works, add HTTPS with a separate TLS virtual host. Obtain and install a trusted certificate, then consider redirecting HTTP requests to HTTPS. The Configure SSL lesson covers the next stage.
- Verify DNS records for every hostname, including any
wwwalias. - Review ownership and permissions before deploying application code.
- Keep access and error logs monitored and rotated.
- Allow only the network ports required by the service.
- Test HTTP-to-HTTPS redirects and certificate coverage for all aliases.