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 notation
Section titled “Variable notation”| Variable name | Example setting | Description |
|---|---|---|
<<SERVER_IP>> | 192.0.2.10 | IP address of the destination web server |
<<SITE1_HOST>> | site1.example.com | Hostname published by the first VirtualHost |
<<SITE2_HOST>> | site2.example.com | Hostname 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.
sudo dnf install -y httpd firewalld policycoreutils-python-utilsStep 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.
set -euo pipefailsudo mkdir -p /srv/www/site1 /srv/www/site2printf '%s\n' '<<SITE1_HOST>>' | sudo tee /srv/www/site1/index.html >/dev/nullprintf '%s\n' '<<SITE2_HOST>>' | sudo tee /srv/www/site2/index.html >/dev/nullsudo chmod -R a+rX /srv/www/site1 /srv/www/site2Step 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.
set -euo pipefailsudo 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/site2sudo ls -Zd /srv/www/site1 /srv/www/site2Use ls -Z to confirm that both DocumentRoots have the httpd_sys_content_t label.
Step 4: Define two VirtualHosts
Section titled “Step 4: Define two VirtualHosts”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.
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>EOFStep 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.
sudo apachectl configtestConfirm 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.
set -euo pipefailsudo 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.
Step 7: Allow HTTP through firewalld
Section titled “Step 7: Allow HTTP through firewalld”Enable firewalld, permanently allow the HTTP service, and then apply the setting to the runtime configuration.
set -euo pipefailsudo systemctl enable --now firewalldsudo firewall-cmd --permanent --add-service=httpsudo firewall-cmd --reloadsudo firewall-cmd --query-service=httpIf the final check returns yes, the HTTP service is allowed in the current firewalld configuration.
Step 8: Start Apache
Section titled “Step 8: Start Apache”After the configuration check is complete, start Apache and enable it to start automatically after a reboot.
sudo systemctl enable --now httpdsudo systemctl is-active httpdIf 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.
sudo sh -c 'setenforce 1 && getenforce'Confirm that Enforcing is displayed.
Step 10: Access the first VirtualHost
Section titled “Step 10: Access the first VirtualHost”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.
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.
Step 11: Access the second VirtualHost
Section titled “Step 11: Access the second VirtualHost”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.
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.
Troubleshooting
Section titled “Troubleshooting”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.