How to Set Up Multiple Websites on Apache

How to set up multiple websites on Apache

How to set up multiple websites on Apache

I run several projects off a single VPS, and virtual hosting is the reason I’ve never had to pay for a separate server per site. Apache handles this natively — one IP address, one Apache instance, and as many independent websites as the hardware can comfortably serve. Here’s exactly how I set it up.

Name-Based vs. IP-Based Virtual Hosting

I’ll focus on name-based virtual hosting here, since that’s what covers essentially every deployment I set up.

Prerequisites

Step 1: Create Directory Structure for Each Site

sudo mkdir -p /var/www/site1.com/public_html
sudo mkdir -p /var/www/site2.com/public_html

echo "<h1>Welcome to Site 1</h1>" | sudo tee /var/www/site1.com/public_html/index.html
echo "<h1>Welcome to Site 2</h1>" | sudo tee /var/www/site2.com/public_html/index.html

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

Step 2: Create a Virtual Host File for Each Site

Debian/Ubuntu — I create files under /etc/apache2/sites-available/:

/etc/apache2/sites-available/site1.com.conf:

<VirtualHost *:80>
    ServerName site1.com
    ServerAlias www.site1.com
    DocumentRoot /var/www/site1.com/public_html

    <Directory /var/www/site1.com/public_html>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/site1.com-error.log
    CustomLog ${APACHE_LOG_DIR}/site1.com-access.log combined
</VirtualHost>

/etc/apache2/sites-available/site2.com.conf:

<VirtualHost *:80>
    ServerName site2.com
    ServerAlias www.site2.com
    DocumentRoot /var/www/site2.com/public_html

    <Directory /var/www/site2.com/public_html>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/site2.com-error.log
    CustomLog ${APACHE_LOG_DIR}/site2.com-access.log combined
</VirtualHost>

RHEL/CentOS — I create equivalent files under /etc/httpd/conf.d/, e.g. site1.com.conf and site2.com.conf, using the same block structure. RHEL doesn’t use the sites-available/sites-enabled split by default — files dropped directly into conf.d/ are active automatically.

Step 3: Enable the Sites (Debian/Ubuntu)

sudo a2ensite site1.com.conf
sudo a2ensite site2.com.conf
sudo a2dissite 000-default.conf   # optional: disable the default placeholder site

Step 4: Test and Reload

sudo apachectl configtest
sudo systemctl reload apache2

Step 5: Verify Each Site Resolves Correctly

curl -I -H "Host: site1.com" http://localhost/
curl -I -H "Host: site2.com" http://localhost/

Or, once DNS has propagated, I just pull up each domain in a browser.

How Apache Decides Which Site to Serve

For name-based virtual hosting, Apache matches incoming requests to a <VirtualHost> block in this order:

  1. It looks for an exact match on ServerName or ServerAlias against the Host header.
  2. If nothing matches, it falls back to the first <VirtualHost> block defined for that IP:port combination — this quietly becomes the default site.

That fallback behavior tripped me up early on: a request arriving with an unrecognized Host header (someone hitting the raw IP, or a misconfigured DNS record) gets served by whichever virtual host happens to be defined first. I define a catch-all default virtual host now to control this explicitly:

<VirtualHost *:80>
    ServerName default-catchall
    DocumentRoot /var/www/default
    <Location />
        Require all denied
    </Location>
</VirtualHost>

I place this block first (alphabetically or by load order) so it becomes the fallback for anything unmatched.

Adding HTTPS for Multiple Sites (SNI)

Modern TLS supports SNI, letting multiple HTTPS sites with different certificates share one IP — no dedicated IP per site required.

sudo certbot --apache -d site1.com -d www.site1.com
sudo certbot --apache -d site2.com -d www.site2.com

Certbot automatically creates and manages a separate <VirtualHost *:443> block with its own certificate for each domain, alongside the existing port 80 blocks (which it can update to redirect to HTTPS too). I’ve got a full post on SSL/HTTPS setup if you want the details.

Using ServerAlias for Multiple Domain Names on One Site

If several domains or subdomains should serve identical content:

<VirtualHost *:80>
    ServerName site1.com
    ServerAlias www.site1.com site1.net site1.org
    DocumentRoot /var/www/site1.com/public_html
</VirtualHost>

Wildcard Subdomains

To handle any subdomain of a domain within a single virtual host (common for multi-tenant setups):

<VirtualHost *:80>
    ServerName example.com
    ServerAlias *.example.com
    DocumentRoot /var/www/example.com/public_html

    # Optional: use %1 to route based on subdomain, combined with mod_rewrite
</VirtualHost>

This needs a wildcard DNS record (*.example.com) pointing at the server.

Real-World Use Cases

Mistakes I’ve Made

Mistakes I’ve Made (Permissions & Structure)

Security Best Practices

Performance Optimization

Troubleshooting

All domains show the same site’s content I check that ServerName is correctly and uniquely set in each virtual host block, and that DNS for each domain actually points at this server.

New site returns default Apache “It works!” page The site probably isn’t enabled (a2ensite), or Apache wasn’t reloaded after I added the config.

“www” version doesn’t work but bare domain does I add ServerAlias www.yourdomain.com and confirm a DNS record exists for the www subdomain.

One site’s SSL certificate error affects browsing another site I confirm each <VirtualHost *:443> block has its own correctly matched SSLCertificateFile/SSLCertificateKeyFile, and that SNI is functioning (nearly universal in modern browsers, though very old clients may not support it).

FAQs

How many websites can one Apache server host? No hard limit from Apache itself; the practical ceiling comes from server resources (RAM, CPU, disk I/O) and combined traffic across all hosted sites.

Do all sites need to share the same PHP/application version? No — with PHP-FPM using separate pools per site (or containerization), different sites on the same Apache instance can run different PHP versions or stacks independently.

Is name-based virtual hosting less secure than one server per site? Not inherently in my experience, but sites do share underlying OS-level resources. Proper isolation — file permissions, process isolation where it matters — mitigates most cross-site risk.

Summary and Key Takeaways

References

Exit mobile version