Apache HTTP Server course

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.test from /var/www/example.test
  • other.test from /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

DirectivePurposeExample valueRequired or optional
ServerAdminAdministrative contact associated with the site configuration.webmaster@example.testRecommended
ServerNamePrimary fully qualified hostname Apache uses to match requests.example.testRequired for predictable name matching
ServerAliasAdditional hostnames served by the same virtual host.www.example.testOptional
DocumentRootDirectory containing the site's public files./var/www/example.testRequired
ErrorLogDestination for server-side errors from this virtual host.${APACHE_LOG_DIR}/example.test-error.logRecommended
CustomLogDestination and format for requests handled by this virtual host.${APACHE_LOG_DIR}/example.test-access.log combinedRecommended

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

CommandPurposeWhen to use it
a2ensite example.test.confEnables an available site.After creating or changing a site that should be active.
a2dissite 000-default.confDisables an enabled site.When a site should no longer answer requests.
apache2ctl configtestChecks Apache configuration syntax.Before every reload or restart.
systemctl reload apache2Loads configuration changes with minimal disruption.After a successful syntax check.
systemctl restart apache2Stops and starts Apache.When a restart is specifically required.
apache2ctl -SLists active virtual hosts, listeners, and name mappings.When verifying routing or diagnosing the wrong site.

Verify the virtual host

  1. Confirm that the hostname resolves to the expected server IP.
  2. Open http://example.test/ in a browser.
  3. Confirm that the page says example.test is working.
  4. Run sudo apache2ctl -S and confirm that the new name appears under the expected listener.
  5. Review example.test-access.log and example.test-error.log if 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.conf succeeded.
  • Run sudo apache2ctl configtest, then reload Apache.
  • Make sure the browser hostname exactly matches ServerName or ServerAlias.
  • Use the explicit Host header with curl to separate Apache routing from DNS problems.
  • Run sudo apache2ctl -S to inspect enabled mappings. An unmatched request may be handled by the default site.

Apache will not reload or restart

  • Run sudo apache2ctl configtest and correct the reported file and line.
  • Check that directives are inside the VirtualHost block 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 Directory rule denies access.
  • Review the virtual host's error log.

A 404 Not Found response appears

  • Compare DocumentRoot with the actual directory path.
  • Confirm that /var/www/example.test/index.html exists 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 www alias.
  • 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.