Virtual Hosts In Depth: Name-Based, IP-Based & SNI

Apache In Depth

Chapter 5 · Virtual Hosts In Depth: Name-Based, IP-Based & SNI

Web Servers Fundamentals Chapter 4 showed one <VirtualHost> block matched by ServerName/ServerAlias, on a single IP, over plain HTTP. That example quietly skipped three real questions: what happens when a server has more than one IP address, how HTTPS makes this whole mechanism dramatically harder, and what actually happens when a request's Host header doesn't match anything at all.

Name-Based Virtual Hosting (What Fundamentals Already Showed)

Every distinct <VirtualHost> sharing the same IP:port is matched purely by the Host header, exactly as Chapter 4 showed. One historical footnote worth knowing if working from an older tutorial: pre-2.4 Apache required an explicit NameVirtualHost *:80 line to turn this matching on at all. Apache 2.4 removed that requirement — any IP:port combination shared by more than one <VirtualHost> automatically becomes name-based. A leftover NameVirtualHost line from an old config isn't fatal on 2.4 — Apache logs a deprecation warning at startup and ignores it — but it's dead weight worth removing.

IP-Based Virtual Hosting

<VirtualHost 192.0.2.10:80> ServerName site-a.example DocumentRoot /var/www/site-a </VirtualHost> <VirtualHost 192.0.2.11:80> ServerName site-b.example DocumentRoot /var/www/site-b </VirtualHost>

IP-based hosting is a genuinely different mechanism, not just a variant of the same one: it distinguishes requests by which IP address they arrived on, not by the Host header at all — this requires the server to actually have multiple IP addresses bound to it (multiple NICs, or IP aliases on one interface). It's rare in modern deployments for the exact reason covered next: the historical problem that made IP-based hosting genuinely necessary for HTTPS has since been solved.

The Historical Problem: HTTPS Broke Name-Based Hosting

A TLS handshake — negotiating encryption and presenting a certificate — happens before any HTTP request, including the Host header, is ever sent; it has to, since the request itself needs to be encrypted using whatever the handshake just negotiated. That created a real chicken-and-egg problem for HTTPS name-based hosting: with several <VirtualHost> blocks sharing one IP, each potentially needing a different TLS certificate, Apache had no way to know which certificate to present during the handshake, since the only thing that would have told it which site was intended — the Host header — hadn't arrived yet. For years, the only real fix was IP-based hosting: one dedicated IP address per HTTPS domain, so the IP itself told Apache which certificate to present.

SNI: How Modern HTTPS Solves This

Server Name Indication (SNI) is a TLS extension that adds the target hostname into the very first message of the handshake — the ClientHello — in plain text, before encryption is even established. That's enough information for Apache to pick the correct <VirtualHost>, and therefore the correct certificate, before the rest of the handshake proceeds — solving the historical problem directly, and making genuinely name-based HTTPS hosting on a single IP the normal case today. Client support is effectively universal now (only truly ancient or specialized embedded clients lack it), which is exactly what makes Chapter 6's per-<VirtualHost> mod_ssl configuration possible in the first place.

The Default/Catch-All VirtualHost

When a request's Host header matches none of the ServerName/ServerAlias entries for the matching IP:port, Apache doesn't return an error — it silently serves whichever <VirtualHost> was defined first for that IP:port, in the order the config files were parsed (commonly alphabetical, for a typical sites-enabled/ setup). This is one of the most common real-world Apache surprises: automated scanners and bots routinely probe a server by raw IP address rather than any real domain name, and whatever site happens to load first in the config silently answers every one of those requests.

An unmatched Host header isn't an error — it's whichever site loads first
It's easy to assume a request for an unrecognized domain name simply fails, the way a typo'd URL might. It doesn't — it's served by the first-defined <VirtualHost> for that IP:port, with no warning that anything unusual happened. A common, deliberate fix is defining an explicit dummy <VirtualHost> block first in the load order — matching nothing meaningful, and configured to return a 403 or close the connection — specifically so an internal, staging, or simply the "wrong" site never becomes the accidental public face of unmatched traffic.
Name-basedIP-based
Distinguishes byHost headerWhich IP address the request arrived on
RequiresNothing extra — one IP is enoughMultiple IP addresses on the server
Works with HTTPS on one IP?Yes, via SNIYes, always — no SNI needed at all
Common today?Yes — the normal caseRare — mostly legacy or specialized isolation needs
Checking the real matching order, not just reading the config
apachectl -S dumps Apache's own fully-parsed virtual host configuration — every IP:port combination, which <VirtualHost> block matches each one, and critically, which one is currently acting as the default for unmatched requests. This is the practical way to confirm the real matching order this chapter describes, rather than manually tracing through include order across multiple config files by hand.

Hands-On Exercises

Exercise 1

Two <VirtualHost 203.0.113.5:80> blocks share the same IP:port — site-a.example is defined first in the config, site-b.example second. A request arrives with Host: unknown-domain.example. Which site serves this request, and why?

📄 View solution
Exercise 2

Explain, in your own words, why hosting multiple HTTPS domains on a single IP address was impossible before SNI existed, and specifically what information SNI adds, and when, to solve it.

📄 View solution
Exercise 3

A team migrating an old Apache 2.2 tutorial's config to a modern Apache 2.4 server includes a leftover "NameVirtualHost *:80" line. Explain what actually happens when Apache starts up with this line present — is it a fatal error, and is it still doing anything useful?

📄 View solution

Chapter 5 Quick Reference

  • Name-based — matched by Host header; the normal, modern case; needs SNI to work over HTTPS with multiple certs on one IP
  • IP-based — matched by which IP the request arrived on; needs multiple real IPs; rare today
  • SNI — the hostname is sent in the TLS ClientHello, before encryption, letting Apache pick the right certificate before the handshake finishes
  • An unmatched Host header is silently served by the first-defined <VirtualHost> for that IP:port — not an error
  • apachectl -S shows the real, fully-parsed matching order
  • NameVirtualHost is deprecated/ignored (with a warning) on Apache 2.4+, not required or fatal