Skip to content

Configure VirtualHosts with Apache HTTP Server

Apache HTTP Server can use name-based VirtualHosts to publish different web content for different hostnames while sharing the same IP address and TCP/80.

In this article, we configure two VirtualHosts on an RHEL 8-compatible Linux system. The DocumentRoots are separated under /srv/www, and SELinux is set to Enforcing when HTTP connectivity is verified. Before applying the configuration, we check its syntax. After allowing HTTP through firewalld, we specify each hostname from a client and verify the returned content.

Variable nameExample settingDescription
<<SERVER_IP>>192.0.2.10IP address of the destination web server
<<SITE1_HOST>>site1.example.comHostname published by the first VirtualHost
<<SITE2_HOST>>site2.example.comHostname published by the second VirtualHost

In a production environment, configure DNS so that <<SITE1_HOST>> and <<SITE2_HOST>> resolve to the server’s IP address. For the verification in this article, the destination of the HTTP request and the Host name are specified explicitly, allowing you to verify name-based VirtualHost routing without changing DNS.

Step 1: Install Apache and SELinux management tools

Section titled “Step 1: Install Apache and SELinux management tools”

Install Apache HTTP Server, firewalld, and the management tools required to persistently configure SELinux labels for non-standard DocumentRoots.

Terminal window
sudo dnf install -y httpd firewalld policycoreutils-python-utils

Step 2: Create a DocumentRoot for each VirtualHost

Section titled “Step 2: Create a DocumentRoot for each VirtualHost”

Use separate DocumentRoots for the two sites and place sample content that makes it possible to identify which VirtualHost returned the response.

Terminal window
set -euo pipefail
sudo mkdir -p /srv/www/site1 /srv/www/site2
printf '%s\n' '<<SITE1_HOST>>' | sudo tee /srv/www/site1/index.html >/dev/null
printf '%s\n' '<<SITE2_HOST>>' | sudo tee /srv/www/site2/index.html >/dev/null
sudo chmod -R a+rX /srv/www/site1 /srv/www/site2

Step 3: Configure SELinux labels for the DocumentRoots

Section titled “Step 3: Configure SELinux labels for the DocumentRoots”

Because /srv/www is not Apache’s standard DocumentRoot, define each site persistently as httpd_sys_content_t and apply the labels to the actual files with restorecon.

Terminal window
set -euo pipefail
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www/site1(/.*)?'
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www/site2(/.*)?'
sudo restorecon -Rv /srv/www/site1 /srv/www/site2
sudo ls -Zd /srv/www/site1 /srv/www/site2

Use ls -Z to confirm that both DocumentRoots have the httpd_sys_content_t label.

Create /etc/httpd/conf.d/virtualhosts.conf and assign a different DocumentRoot to each hostname on the same TCP/80. To allow access to the non-standard DocumentRoots, permit access in each corresponding Directory section.

Terminal window
sudo tee /etc/httpd/conf.d/virtualhosts.conf >/dev/null <<'EOF'
ServerName <<SITE1_HOST>>
<VirtualHost *:80>
ServerName <<SITE1_HOST>>
DocumentRoot "/srv/www/site1"
<Directory "/srv/www/site1">
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName <<SITE2_HOST>>
DocumentRoot "/srv/www/site2"
<Directory "/srv/www/site2">
Require all granted
</Directory>
</VirtualHost>
EOF

Step 5: Check the Apache configuration syntax

Section titled “Step 5: Check the Apache configuration syntax”

Before applying the configuration to the service, check the syntax of the entire Apache configuration, including the VirtualHosts.

Terminal window
sudo apachectl configtest

Confirm that the exit code is 0 and that there are no syntax errors before continuing.

Step 6: Verify that Apache recognizes the VirtualHosts

Section titled “Step 6: Verify that Apache recognizes the VirtualHosts”

Confirm that Apache has loaded both hostnames as VirtualHosts.

Terminal window
set -euo pipefail
sudo apachectl -S 2>&1 | grep -F '<<SITE1_HOST>>'
sudo apachectl -S 2>&1 | grep -F '<<SITE2_HOST>>'

If both hostnames are displayed, the configuration file has been recognized as a VirtualHost configuration.

Enable firewalld, permanently allow the HTTP service, and then apply the setting to the runtime configuration.

Terminal window
set -euo pipefail
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
sudo firewall-cmd --query-service=http

If the final check returns yes, the HTTP service is allowed in the current firewalld configuration.

After the configuration check is complete, start Apache and enable it to start automatically after a reboot.

Terminal window
sudo systemctl enable --now httpd
sudo systemctl is-active httpd

If active is displayed, Apache is running under systemd management.

Step 9: Set SELinux to Enforcing and verify it

Section titled “Step 9: Set SELinux to Enforcing and verify it”

If SELinux is in Permissive mode, switch its current operating state to Enforcing before checking HTTP connectivity.

If you start a new sudo process or run getenforce as a regular user after switching to Enforcing, the verification itself may fail in some environments because of SELinux restrictions immediately after the change. Therefore, start a shell with administrative privileges before switching to Enforcing, and perform both the switch and the verification within that same shell.

Terminal window
sudo sh -c 'setenforce 1 && getenforce'

Confirm that Enforcing is displayed.

From the client, access the first hostname over HTTP. Because --resolve explicitly specifies both the destination IP address and the Host name, you can verify name-based VirtualHost routing even before registering the hostname in DNS.

Terminal window
curl -fsS --resolve "<<SITE1_HOST>>:80:<<SERVER_IP>>" "http://<<SITE1_HOST>>/"

If the response body is <<SITE1_HOST>>, the first VirtualHost is associated with the intended DocumentRoot.

Access TCP/80 on the same server and explicitly specify the second hostname in the HTTP Host header. With name-based VirtualHosts, routing uses not only the destination IP address but also the Host name, so the Host header must be specified explicitly when checking the second site.

Terminal window
curl -fsS -H "Host: <<SITE2_HOST>>" "http://<<SERVER_IP>>/"

If the response body is <<SITE2_HOST>>, content separation by VirtualHost on the same IP address is working correctly.

If HTTP returns 403, check not only the UNIX permissions of the DocumentRoot but also the SELinux labels. /srv/www/site1 and /srv/www/site2 must have the httpd_sys_content_t label.

If an unexpected site is returned, verify that ServerName matches the Host header in the request. When multiple name-based VirtualHosts share the same IP address and port, Apache selects the VirtualHost whose hostname matches the requested hostname.

With this configuration, you can separate multiple sites on a single Apache HTTP Server while keeping SELinux in Enforcing mode and verifying HTTP publication through firewalld.

Category: Linux