Apache virtual hosts are the mechanism by which a single Apache HTTP Server instance serves multiple websites, distinguishing between them using the ServerName directive and the incoming Host HTTP header. Name-based virtual hosting (the most common type) allows hundreds of domains to share a single IP address, with Apache routing each request to the correct document root and configuration based on the domain name. On RHEL 9, virtual host files live in /etc/httpd/conf.d/ as individual .conf files. This guide covers creating HTTP and HTTPS virtual hosts, configuring .htaccess overrides, setting up redirects, implementing custom logging, and testing the configuration with apachectl configtest.

Prerequisites

  • Apache installed and running on RHEL 9 (see: How to Install Apache HTTP Server on RHEL 9)
  • Domain names pointing to your server’s IP (or entries in /etc/hosts for testing)

Step 1 — Create Document Roots

mkdir -p /var/www/site1.com/html
mkdir -p /var/www/site2.com/html

chown -R apache:apache /var/www/site1.com /var/www/site2.com
chmod -R 755 /var/www/site1.com /var/www/site2.com

echo '

site1.com — Apache on RHEL 9

' > /var/www/site1.com/html/index.html echo '

site2.com — Apache on RHEL 9

' > /var/www/site2.com/html/index.html # Set SELinux file context semanage fcontext -a -t httpd_sys_content_t '/var/www/site1.com(/.*)?' semanage fcontext -a -t httpd_sys_content_t '/var/www/site2.com(/.*)?' restorecon -Rv /var/www/site1.com /var/www/site2.com

Step 2 — Create an HTTP Virtual Host

vi /etc/httpd/conf.d/site1.com.conf

    ServerName   site1.com
    ServerAlias  www.site1.com
    DocumentRoot /var/www/site1.com/html
    ServerAdmin  [email protected]

    
        Options    -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    

    CustomLog /var/log/httpd/site1.com.access_log combined
    ErrorLog  /var/log/httpd/site1.com.error_log

Step 3 — Create an HTTPS Virtual Host with HTTP Redirect

vi /etc/httpd/conf.d/site2.com.conf
# Redirect HTTP to HTTPS

    ServerName  site2.com
    ServerAlias www.site2.com
    Redirect permanent / https://site2.com/


# HTTPS virtual host

    ServerName   site2.com
    ServerAlias  www.site2.com
    DocumentRoot /var/www/site2.com/html
    ServerAdmin  [email protected]

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/site2.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/site2.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/site2.com/chain.pem

    # Modern TLS only
    SSLProtocol           all -SSLv3 -TLSv1 -TLSv1.1
    SSLHonorCipherOrder   off
    SSLSessionTickets     off

    # Security headers
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    Header always set X-Frame-Options DENY
    Header always set X-Content-Type-Options nosniff

    
        Options    -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    

    CustomLog /var/log/httpd/site2.com.ssl.access_log combined
    ErrorLog  /var/log/httpd/site2.com.ssl.error_log

Step 4 — Redirect www to non-www

# Add a separate block at the top of the conf file

    ServerName www.site1.com
    Redirect permanent / http://site1.com/

Step 5 — Use .htaccess for Per-Directory Rules

When AllowOverride All is set in the <Directory> block, Apache reads .htaccess files in each directory. This enables WordPress, Laravel, and other frameworks to manage their own URL rewriting:

# Example .htaccess for a PHP framework with clean URLs
cat > /var/www/site1.com/html/.htaccess << 'EOF'
Options -MultiViews
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
EOF

Step 6 — Custom Error Pages

# Inside a VirtualHost block
ErrorDocument 404 /404.html
ErrorDocument 500 /500.html

# Create the error pages
echo '

404 — Page not found

' > /var/www/site1.com/html/404.html

Step 7 — Test and Reload

# Always test before reloading
apachectl configtest

# Graceful reload (existing connections finish first)
systemctl reload httpd

# Test that virtual hosts respond correctly
curl -H "Host: site1.com" http://localhost
curl -H "Host: site2.com" http://localhost

Step 8 — List Active Virtual Hosts

# Show all parsed virtual hosts
apachectl -S

# List virtual host conf files
ls -la /etc/httpd/conf.d/

Troubleshooting

  • 403 Forbidden — check SELinux (ls -lZ /var/www/site1.com/html/), directory permissions, and that Require all granted is set in the <Directory> block.
  • Wrong site served — Apache serves the first matching VirtualHost by default. Ensure each ServerName is unique. Use apachectl -S to debug routing.
  • .htaccess not working — check that AllowOverride All is set and mod_rewrite is loaded: httpd -M | grep rewrite.

Conclusion

Apache virtual hosts on RHEL 9 give you granular per-site control over document roots, SSL, rewrites, logging, and access control. Drop-in .conf files in /etc/httpd/conf.d/ keep each site’s configuration isolated, and apachectl -S gives an instant overview of all active virtual hosts and how requests will be routed.

Next steps: How to Secure Apache with Let’s Encrypt on RHEL 9, How to Configure Apache mod_proxy as a Reverse Proxy on RHEL 9, and How to Set Up ModSecurity WAF with Apache on RHEL 9.