Table of Contents
Concept of Virtual Hosts
Virtual hosts let a single web server respond differently based on the hostname or IP address that the client uses. With virtual hosts you can run multiple websites on one server, each with its own domain name, document root, configuration, and even its own SSL certificate.
From the browser perspective the user simply visits http://example.com or https://shop.example.com. The browser sends the hostname in the HTTP request. Apache and Nginx both read this hostname and select the matching virtual host configuration to serve the correct site.
There are three common ideas tied to virtual hosts. Name based virtual hosts which distinguish sites by hostname on the same IP and port. IP based virtual hosts which distinguish sites by the destination IP address. Port based setups where different sites respond on different ports such as :80, :8080, or :443. In modern deployments, name based hosting is the default for HTTP and HTTPS, while IP based or port based arrangements are used mainly for special routing or security needs.
A virtual host definition decides which content and configuration is applied for a given combination of IP, port, and hostname. Only one virtual host will be selected per request.
Virtual Hosts in Apache
Apache uses <VirtualHost> blocks in its configuration to define virtual hosts. Each block defines how Apache should handle a particular IP and port combination, and which hostnames belong to it. A very minimal example looks like this:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com/public_html
</VirtualHost>
Here *:80 means that this vhost listens on any IP on port 80. The ServerName directive sets the primary hostname for this site. ServerAlias defines additional hostnames that will be treated as the same site. DocumentRoot defines the directory Apache will serve files from for requests that match this virtual host.
In name based virtual hosting Apache uses the Host header sent by the client to choose the right <VirtualHost> block. All name based virtual hosts must share the same IP and port combination. Apache decides which virtual host is the default by the order in which the <VirtualHost> blocks appear. The first one that matches an IP and port becomes the default if no hostname or an unknown hostname is requested.
The first <VirtualHost> entry for a given IP and port acts as the default host. Requests that do not match any ServerName or ServerAlias will be served by this default virtual host.
In many distributions Apache stores individual virtual host definitions in separate files, then includes them from a main configuration directory. On Debian and Ubuntu it is common to have configuration files in /etc/apache2/sites-available/ and to enable them with helper tools that manage symbolic links into /etc/apache2/sites-enabled/. On Red Hat family systems the main configuration is often in /etc/httpd/conf/httpd.conf with extra vhosts in /etc/httpd/conf.d/. The exact file layout is distribution specific, but the <VirtualHost> blocks themselves are portable.
Apache supports IP based virtual hosting by placing a specific IP in the <VirtualHost> line instead of *. For example, <VirtualHost 203.0.113.10:80>. In that case, Apache will select that virtual host only for requests that arrive at that IP address and port. If there are several <VirtualHost> blocks with the same IP and port, Apache falls back to name based selection between them using the host header.
It is important to align Apache virtual host configuration with DNS and network settings. The ServerName and ServerAlias values must match the hostnames that the DNS records resolve to the server. Otherwise clients may resolve the domain correctly but Apache will still route the request to its default virtual host because there is no matching name.
For management and troubleshooting, you can use configuration test commands before reloading the service. For Apache this is usually apachectl configtest or apache2ctl configtest depending on the distribution. This checks all <VirtualHost> blocks, syntax, and included files, and reports parsing errors so you can fix them before the server fails to restart.
Virtual Hosts in Nginx
Nginx implements virtual hosting using server blocks inside configuration files. Within a server block you use listen and server_name directives to define the host. The root directive selects the document root. A simple example is:
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com/public_html;
index index.html index.htm;
}
Here listen 80; means this server block accepts traffic on port 80 on any IP. The server_name directive lists all hostnames handled by this block. Nginx compares the Host header from the client against each server_name in the configuration to decide which server block will handle the request.
Nginx supports different forms of matching for server_name. An exact name like example.com, wildcard patterns like *.example.com, and regular expressions with the ~ prefix. Nginx checks for exact matches first, then wildcard and regex matches. A server_name _; or a server_name "" is often used to create a catch all default server.
Nginx selects a default server for each listen address and port combination. By default this is the first server block that listens on that address and port in the configuration file order. You can also set default_server in the listen directive to force a particular block to be the default:
server {
listen 80 default_server;
server_name _;
return 444;
}
In this example any request that does not match a more specific server_name will hit this default server block. The return 444; directive closes the connection without sending a response code, which is one typical way to drop unwanted traffic.
On many systems Nginx main configuration lives in /etc/nginx/nginx.conf and server blocks are placed in files under /etc/nginx/conf.d/ or /etc/nginx/sites-available/ with symlinks to /etc/nginx/sites-enabled/. Nginx includes these with include directives. The concept stays the same. You group each site and its virtual host configuration in its own server block.
You can test Nginx configuration with nginx -t which parses all config files and reports errors, including issues inside server blocks. Only after a successful test should you reload or restart the Nginx service, so that a misconfigured virtual host does not bring the service down.
Name Based vs IP Based and Port Based Hosts
In name based virtual hosting, all sites share the same IP and port, usually :80 for HTTP and :443 for HTTPS. The server uses the HTTP Host header, and for HTTPS also the Server Name Indication (SNI) in the TLS handshake, to decide which configuration applies. This is the most common approach today because IPv4 addresses are scarce and name based hosting allows many domains to share a single IP.
IP based virtual hosting dedicates one IP address per site. Each virtual host is configured to listen on its own IP. The selection is done purely by the destination address, without considering the hostname. This is useful when different SSL configurations or strict isolation is required, although modern SNI support reduces the need for IP based hosting for most use cases.
Port based setups run sites on different ports, such as one site on example.com:80 and another on example.com:8080. While that is a form of virtual hosting, it is less user friendly because visitors must specify non default ports in the URL. It is used sometimes for internal services, administration panels, or when reverse proxying requests through another layer.
For HTTPS virtual hosts the same differentiation applies. With SNI the client indicates which hostname it wants to connect to before the TLS handshake finishes. The web server can then select the correct certificate and configuration for that hostname. Without SNI, you could not present different certificates on the same IP and port, so multiple IP addresses were necessary.
With HTTPS and multiple virtual hosts on the same IP and port, correct SNI support and correct certificate assignment are mandatory. A wrong mapping between hostname, certificate, and virtual host will produce certificate warnings or handshake failures.
Virtual Hosts and SSL Certificates
Virtual hosting interacts closely with TLS configuration. Each site that uses HTTPS needs a certificate that matches its hostname. In Apache, SSL configuration is typically added inside a <VirtualHost *:443> block. For example:
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com/public_html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
</VirtualHost>
Apache will use this certificate only for requests that hit this virtual host. Other HTTPS virtual hosts on the same IP will have their own certificate directives in their own <VirtualHost *:443> blocks.
In Nginx, TLS configuration lives inside the server block that listens on port 443. For example:
server {
listen 443 ssl;
server_name example.com;
root /var/www/example.com/public_html;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
}
Each HTTPS site has its own server block with its own ssl_certificate and ssl_certificate_key. When a client connects and presents example.com in the SNI field, Nginx selects this server block and thus presents the correct certificate.
When multiple domain names share the same certificate, such as a certificate with Subject Alternative Names, you can list them all under the same virtual host as aliases. When they use different certificates you need separate virtual hosts, one per certificate, with matching ServerName or server_name directives.
Testing and Troubleshooting Virtual Hosts
When you configure virtual hosts it is important to verify that the server is returning the expected site for each hostname. You can use the system hosts file to point a domain to a particular IP for local testing by adding entries in /etc/hosts on Linux, or the equivalent on other systems. This allows you to test virtual host behavior before DNS records are updated globally.
Command line tools help confirm which content is served. You can use curl -H "Host: example.com" http://127.0.0.1/ to send a manual request with a specific Host header. This is useful when DNS is not yet set up or when you debug local configurations. Check that the returned HTML, headers, and status codes match the expected virtual host.
If you see the wrong site when visiting a domain it usually indicates that the request is falling back to the default virtual host. This can happen if the hostname in ServerName or server_name does not exactly match the one in the URL, including or excluding www. It can also occur when a catch all virtual host placed earlier in the configuration takes precedence.
Use the process specific configuration test commands before restarting the service. For Apache use apachectl configtest. For Nginx use nginx -t. Both tools will show you where configuration parsing failed, and often print file names and line numbers so you can quickly locate and fix typos, missing semicolons, or misnested blocks.
Log files are also central for diagnosing virtual host issues. Access logs may be split per virtual host or global, depending on your configuration. By reviewing the requested Host header in the logs you can confirm what the server received and which virtual host handled the request. Error logs reveal problems like permission denials, missing files in the document root, or SSL handshake failures that occur when mismatched certificates and hostnames are used.
Organization and Best Practices
To keep multiple virtual hosts maintainable, it is common to place each site in its own configuration file and its own document root directory. Human readable names like /var/www/example.com/ for the site data, and config file names like example.com.conf, help relate configuration to content and DNS entries.
Inside each virtual host configuration you can define site specific directives. For Apache this might include ErrorLog and CustomLog paths, directory options specific to that site, and redirect rules. For Nginx this often includes its own access_log and error_log directives, location blocks for rewriting URLs, and reverse proxy definitions for back end applications. Virtual hosts act as the top level boundary for this per site configuration isolation.
You should make a clear distinction between HTTP and HTTPS virtual hosts. For each domain that supports both, you often configure an HTTP virtual host that only redirects to HTTPS, and a separate HTTPS virtual host that serves the real content. This avoids mixed content issues and simplifies TLS related settings, because you know that all real traffic passes through the HTTPS configuration.
Finally, remember that the web server selects virtual hosts by IP, port, and hostname, but it does not manage DNS, certificates, or application code. Proper virtual host configuration must fit into a wider picture that includes correct DNS records, valid and up to date certificates, and a stable directory structure for frontend assets and application endpoints.