Virtual Hosts
In this page:
Why Virtual Hosts Exist
Without Virtual Hosts, a server can only really serve one site per IP address and port. Virtual Hosts let one Apache instance answer for site-a.com, site-b.com, and blog.site-a.com at the same time, each with its own DocumentRoot, logs, and settings -- exactly what shared hosting and most multi-site servers rely on.
Name-Based Virtual Hosts
The overwhelmingly common setup today is name-based virtual hosting: every site shares the same IP and port, and Apache decides which one to serve by reading the Host header the browser sends (which is just the domain name you typed). A <VirtualHost *:80> block, matched against its ServerName, defines each site.
Anatomy of a VirtualHost Block
A typical block sets ServerName (the primary domain it answers to), optionally ServerAlias (other domains/subdomains that should match the same block, e.g. www.example.com), DocumentRoot (where its files live), and its own ErrorLog/CustomLog paths so each site's logs stay separate.
How Apache Picks a Match
Apache checks incoming requests against each <VirtualHost> block in the order they're loaded, and uses the first one whose ServerName/ServerAlias matches the request's Host header. If nothing matches, it falls back to the very first VirtualHost block defined for that IP/port -- which is why it's common practice to put a deliberate "default" or catch-all site first.
Enabling a New Virtual Host
On Ubuntu, a new site's config is a file in sites-available/, enabled with a2ensite yoursite.conf, then activated with systemctl reload apache2. On RHEL-family systems, the file just needs to live in the included conf.d/ directory and Apache picks it up on the next reload -- there's no separate enable step.
Example: A basic name-based VirtualHost
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>
{# Flagged by hand after confirming a runner can't handle this example
(a shell command / go.mod file stored as a TopicExample, a language
feature the configured runner version doesn't support, or output
that blows a runner's sandbox limit) -- see TopicExample.norun.
Never render the run button for these, regardless of language,
since it would just fail at execute_code (or worse, hang the
Judge0 queue on a submission that can never finish cleanly). #}
- Forgetting
ServerNameon a VirtualHost block, which leaves Apache guessing and can cause it to log a startup warning or match requests unpredictably. - Defining two VirtualHost blocks with the exact same ServerName, so the second one never actually gets used.
- Editing the VirtualHost file but forgetting to reload Apache (or, on Ubuntu, forgetting
a2ensitefirst) -- the new site simply won't be reachable yet.
Chapter Quiz — Complete all 4 topics to unlock
0/4 topics done
Complete these topics first: