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:
- A legacy application or client requires a truly dedicated IP per site
- I need to isolate traffic at the network level for compliance or security reasons
- Certain older clients or devices don’t support SNI or the
Hostheader reliably - I’m hosting services (like some mail or FTP-adjacent setups) where IP-level separation is expected
Prerequisites
- Apache installed on Linux
- Multiple IP addresses available on the server (either physical NICs or IP aliases on one interface)
- Root or sudo access
- Basic networking knowledge (subnetting, interface configuration)
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
- I apply firewall rules (
ufw,iptables, or cloud security groups) per IP address when true network-level isolation is the goal — IP-based vhosts alone don’t provide firewalling. - I keep separate logs per IP-based vhost to simplify auditing, especially important in compliance-driven use cases.
- I avoid mixing sensitive, isolated services with general-purpose sites on the same physical interface when strict separation is required.
Performance Optimization Tips
- IP-based virtual hosting doesn’t inherently perform better or worse than name-based hosting for typical traffic — the real performance factor is usually the application layer, not the routing mechanism.
- If dedicating IPs for isolation purposes, I still apply standard Apache performance practices (proper MPM configuration, caching headers, keep-alive tuning) to each vhost individually.
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.
