Apache virtual hosts let one Linux server deliver different websites for different domain names. The browser sends a hostname, and Apache matches it to the correct ServerName, document root, and log files.
This guide shows how to configure Apache virtual hosts on Rocky Linux 9, AlmaLinux 9, RHEL 9, and CentOS Stream 9. It keeps SELinux enabled, uses the standard EL9 configuration layout, tests every change, and explains how to add HTTPS afterward.
How Name-Based Virtual Hosts Work
Name-based virtual hosting lets several domains share the same IP address and ports. Each domain’s DNS points to the server. Apache first finds virtual hosts listening on the requested address and port, then compares the request hostname with ServerName and ServerAlias.
If no name matches, Apache uses the first virtual host for that address and port. For that reason, configuration order and a deliberate default site matter.
Prerequisites
- A supported EL9 server with sudo access
- Two domains or subdomains for testing
- DNS A/AAAA records pointing to this server
- Ports 80 and 443 allowed by the provider and local firewall
- A backup of existing Apache configuration and website data
The examples use example.com and example.net. Replace them everywhere with domains you control.
Install and Start Apache on EL9
sudo dnf update -y sudo dnf install -y httpd policycoreutils-python-utils sudo systemctl enable --now httpd sudo systemctl status httpd --no-pager
The Apache package and service are called httpd on EL9. You do not need EPEL just to install Apache. Check the current configuration before editing it:
sudo apachectl configtest
Open HTTP and HTTPS in firewalld
If firewalld is active, use its named web services:
sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload sudo firewall-cmd --list-services
Keep SSH access allowed before changing a remote firewall. A cloud provider may also have a separate network firewall.
Create Separate Document Roots
sudo mkdir -p /var/www/example.com/public_html sudo mkdir -p /var/www/example.net/public_html
Set ownership to the person or deployment account that maintains the files. Apache only needs read access for a static site:
sudo chown -R $USER:$USER /var/www/example.com /var/www/example.net
sudo find /var/www/example.com /var/www/example.net -type d -exec chmod 755 {} \;
sudo find /var/www/example.com /var/www/example.net -type f -exec chmod 644 {} \;
Avoid chmod -R 777. Applications that need write access should receive it only for their specific upload, cache, or data directories.
Add a Test Page for Each Domain
echo '<h1>example.com is working</h1>' | sudo tee /var/www/example.com/public_html/index.html echo '<h1>example.net is working</h1>' | sudo tee /var/www/example.net/public_html/index.html
An index.html file is convenient for testing, but Apache can serve many file types and applications. The application and DirectoryIndex settings decide the entry file.
Keep SELinux Enabled
Do not disable SELinux to make Apache work. Content under /var/www normally receives the correct httpd_sys_content_t label, but restore and verify it after copying files:
sudo restorecon -Rv /var/www/example.com /var/www/example.net ls -Zd /var/www/example.com/public_html /var/www/example.net/public_html
If you place website data outside /var/www, define a persistent SELinux file-context rule instead of using a temporary chcon workaround:
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/example.com(/.*)?' sudo restorecon -Rv /srv/example.com
Create Apache Virtual Host Files
On EL9, Apache automatically includes files ending in .conf from /etc/httpd/conf.d/. There is no need to copy Debian’s sites-available layout or edit the main file just to include a site.
Create the first file:
sudo nano /etc/httpd/conf.d/example.com.conf
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot "/var/www/example.com/public_html"
<Directory "/var/www/example.com/public_html">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
ErrorLog /var/log/httpd/example.com_error.log
CustomLog /var/log/httpd/example.com_access.log combined
</VirtualHost>
Create /etc/httpd/conf.d/example.net.conf with the matching domain and path:
<VirtualHost *:80>
ServerName example.net
ServerAlias www.example.net
DocumentRoot "/var/www/example.net/public_html"
<Directory "/var/www/example.net/public_html">
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
</Directory>
ErrorLog /var/log/httpd/example.net_error.log
CustomLog /var/log/httpd/example.net_access.log combined
</VirtualHost>
Options -Indexes prevents a directory listing when an index file is absent. Keep AllowOverride None unless the application genuinely uses .htaccess; then allow only the features it needs.
Test Before Reloading Apache
sudo apachectl configtest sudo apachectl -S
The test must say Syntax OK. The -S output shows which domain maps to each file and which virtual host is the default. When it is correct, reload without dropping active connections:
sudo systemctl reload httpd
Test Before DNS Is Live
You can send a temporary Host header from the server:
curl -H 'Host: example.com' http://127.0.0.1/ curl -H 'Host: example.net' http://127.0.0.1/
This confirms Apache’s matching, but it does not test public DNS or external firewalls. After DNS changes, use dig and test each public URL.
Add HTTPS to Every Virtual Host
Get a trusted certificate only after the domain resolves publicly and HTTP works. Use your organization’s approved ACME client or certificate process. Back up the Apache files, check what the client will change, and keep separate HTTPS configuration for each site.
Afterward, verify the certificate, HTTP-to-HTTPS redirect, renewal timer, and configuration:
sudo apachectl configtest sudo systemctl reload httpd curl -I https://example.com curl -I https://example.net
Troubleshooting Virtual Hosts
The Wrong Website Appears
Run apachectl -S. Check DNS, ServerName, ServerAlias, configuration order, and whether the request used the expected hostname.
Apache Returns 403 Forbidden
Check directory traversal permissions, the <Directory> block, SELinux labels, and the error log. Do not disable SELinux or grant world-write permission as a shortcut.
Apache Will Not Reload
Run apachectl configtest and inspect journalctl -u httpd. Restore the backed-up configuration if needed.
PHP or an Application Needs to Write Files
Give the runtime access only to required directories and apply the correct SELinux writable-content label. Follow the application’s and Red Hat’s security documentation rather than changing the whole document root to 777.
Frequently Asked Questions
How many domains can one Apache server host?
Apache can host many name-based sites on one IP. The practical limit comes from CPU, RAM, storage, traffic, application load, administration, and security requirements.
Should SELinux be disabled for Apache?
No. Keep it enforcing and apply the proper file labels and booleans for the site’s actual needs.
Where do EL9 virtual-host files go?
A clean approach is one .conf file per site under /etc/httpd/conf.d/, which the standard configuration includes automatically.
Conclusion
You have configured two Apache name-based virtual hosts on EL9, kept SELinux protection, tested domain matching, and prepared the sites for HTTPS. Repeat the directory, configuration, validation, DNS, and certificate steps for each additional domain.
For the next security steps, see our server hardening checklist and SSH configuration guide.
Reviewed and updated: August 2026. The procedure was checked against Red Hat’s official RHEL 9 web-server guide and Apache’s official name-based virtual-host documentation.











Comments