← Back to Apache Course | Chapter 2: Configuration Basics | Lesson 2 of 4

Virtual Hosts

A single Apache server can host many different websites at once -- Virtual Hosts are the mechanism that lets Apache tell those sites apart and serve each one from its own directory with its own settings.

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.

Warning: Two VirtualHost blocks with the same ServerName (or an overlapping ServerAlias) silently mean the second one is effectively unreachable -- Apache won't warn you, it just always matches the 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

apacheconf
<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). #}

⚠️ This example can't run in the browser editor. Try it in your own local environment instead.

{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting ServerName on a VirtualHost block, which leaves Apache guessing and can cause it to log a startup warning or match requests unpredictably.
  2. Defining two VirtualHost blocks with the exact same ServerName, so the second one never actually gets used.
  3. Editing the VirtualHost file but forgetting to reload Apache (or, on Ubuntu, forgetting a2ensite first) -- 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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.