How to Create IP-Based Virtual Hosts in Apache

How to create IP-based virtual hosts in Apache

How to create IP-based virtual hosts in Apache

Most of the Apache setups I do these days are name-based, but every so often I run into a situation that genuinely calls for IP-based virtual hosts — usually something involving legacy client requirements, strict network isolation, or an old application that identifies sites by IP rather than hostname. When that happens, I fall back on a technique that predates SNI entirely. In this post, I’ll walk through exactly how I set up IP-based virtual hosts in Apache.

What IP-Based Virtual Hosts Are and Why They Matter

Unlike name-based virtual hosting, where Apache picks a site based on the Host header the browser sends, IP-based virtual hosting selects a site purely based on which IP address the request arrived on. Each site is bound to its own distinct IP address, and Apache doesn’t need to inspect the hostname at all to decide which content to serve.

I reach for this approach when:

Prerequisites

Step 1: Confirm You Have Multiple IP Addresses Available

I check what’s currently assigned to the server:

ip addr show

If I only have one IP and need more, I either request additional IPs from my hosting provider or add IP aliases to an existing network interface. On most cloud providers, this is done through their control panel; on a bare interface, I can add one manually (this is often not persistent across reboots unless configured properly in netplan or the relevant network config):

sudo ip addr add 203.0.113.11/24 dev eth0

For a persistent Netplan-based configuration:

network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 203.0.113.10/24
        - 203.0.113.11/24

Then apply it:

sudo netplan apply

Step 2: Confirm Apache Is Listening on Each IP

By default, Apache listens on all interfaces with Listen 80. I don’t need to change this for IP-based hosting — I just need to make sure each virtual host is scoped to a specific IP rather than a wildcard.

Step 3: Create IP-Based Virtual Host Blocks

Instead of using *:80, I bind each <VirtualHost> directly to a specific IP address:

<VirtualHost 203.0.113.10:80>
    DocumentRoot /var/www/site1/public_html

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

    ErrorLog ${APACHE_LOG_DIR}/site1_error.log
    CustomLog ${APACHE_LOG_DIR}/site1_access.log combined
</VirtualHost>

<VirtualHost 203.0.113.11:80>
    DocumentRoot /var/www/site2/public_html

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

    ErrorLog ${APACHE_LOG_DIR}/site2_error.log
    CustomLog ${APACHE_LOG_DIR}/site2_access.log combined
</VirtualHost>

Notice there’s no ServerName doing the routing work here — the IP address in the <VirtualHost> tag itself is what determines which block handles which request. I can still add ServerName for logging and canonical URL generation purposes, but it isn’t what Apache uses to route the request in this model.

Step 4: Enable the Sites and Reload Apache

sudo a2ensite site1.conf
sudo a2ensite site2.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Step 5: Verify Each IP Serves the Correct Site

I test each IP address directly:

curl -I http://203.0.113.10
curl -I http://203.0.113.11

Each should return content from its respective site, completely independent of any Host header I send (or don’t send).

Step 6: Point DNS Records to the Correct IPs

Once I confirm IP-based routing works correctly, I update DNS so each domain points at its dedicated IP:

site1.com.    A    203.0.113.10
site2.com.    A    203.0.113.11

Real-World Example: Isolating a Legacy Payment Gateway Integration

I once worked with a legacy payment gateway integration that whitelisted a specific server IP for callback requests, and the client wanted that traffic completely isolated from their main marketing site for compliance reasons. I dedicated a separate IP purely to that integration’s virtual host, with its own strict firewall rules, separate from the rest of the server’s name-based sites. IP-based hosting made that isolation clean and auditable.

Step 7: Combining IP-Based Hosting with Firewall Rules for True Isolation

IP-based virtual hosting on its own only controls which Apache virtual host answers a request — it doesn’t provide network-level isolation by itself. When true isolation is the actual goal (which it usually is when someone asks for IP-based hosting specifically), I pair it with firewall rules scoped to each individual IP:

sudo ufw allow in on eth0 to 203.0.113.11 port 80 proto tcp
sudo ufw deny in on eth0 to 203.0.113.11 port 8080 proto tcp

This lets me restrict exactly which ports and protocols are reachable on each dedicated IP, layering real network security on top of Apache’s routing behavior rather than relying on virtual host configuration alone to provide isolation it was never designed to guarantee.

Step 8: Migrating from IP-Based Back to Name-Based Hosting

Occasionally a legacy requirement that justified IP-based hosting goes away, and I migrate a site back to name-based virtual hosting to free up IP addresses for other uses. The process is straightforward: I change the <VirtualHost> binding from a specific IP back to a wildcard, add the appropriate ServerName and ServerAlias directives, confirm DNS still resolves correctly, and test thoroughly with apache2ctl -S before decommissioning the old dedicated IP.

Troubleshooting Common Issues

Both sites show the same content This usually means both virtual hosts are still using a wildcard (*:80) instead of a specific IP, or the second IP address isn’t actually bound to the network interface. Recheck with ip addr show.

Second IP not responding at all Confirm the IP is actually configured on the interface and that any firewall (like ufw or cloud security groups) allows traffic to it.

Apache fails to start after adding IP-based vhosts Run apache2ctl configtest — a common cause is referencing an IP address that isn’t actually bound to any interface on the server yet.

Mixing IP-based and name-based vhosts causes conflicts Be careful combining both approaches on the same IP — if one vhost uses a wildcard IP and another uses a specific IP that overlaps, Apache’s matching behavior can get confusing. I keep IP-based and name-based hosting cleanly separated where possible.

Security Best Practices

Performance Optimization Tips

Frequently Asked Questions

Is IP-based virtual hosting still necessary today? Rarely, for typical websites — SNI-based name hosting solved the historical IP-scarcity problem. IP-based hosting today is mostly used for specific compliance, legacy compatibility, or network isolation requirements.

Can I combine IP-based and name-based virtual hosts on the same server? Yes, as long as they’re scoped to different IP addresses, or you’re careful about how wildcard and specific IP bindings interact.

Do I need multiple physical network cards for multiple IPs? No — IP aliasing lets you assign multiple IP addresses to a single physical interface.

Does IP-based hosting work with HTTPS/SNI? Yes, and in fact IP-based binding paired with dedicated certificates per IP was the standard method for multi-site HTTPS before SNI existed. It still works today, just less commonly needed.

Step 9: Documenting IP Allocations Carefully

With multiple dedicated IPs in play, I keep a clear internal record of which IP belongs to which site, which firewall rules apply to it, and why it needed to be IP-based in the first place. IP-based hosting is uncommon enough today that six months later, without documentation, it’s easy to forget the original reasoning and accidentally “simplify” a setup back to name-based hosting in a way that breaks the exact compliance or compatibility requirement it was built to satisfy.

Summary and Key Takeaways

IP-based virtual hosting is a niche but still genuinely useful tool — mostly for legacy compatibility, strict network isolation, or compliance-driven separation between sites. The core technique is simple: bind each <VirtualHost> block to a specific IP instead of a wildcard, make sure that IP is actually configured on the server’s network interface, and verify routing with direct curl tests against each IP before pointing DNS at it.

References

Exit mobile version