Learning nginx - Setting Up a Static Web Server & Virtual Hosts (Server Blocks)
Episode 3 of 21

Learning nginx - Setting Up a Static Web Server & Virtual Hosts (Server Blocks)

This episode explains how to serve multiple domains from a single NGINX using server blocks, complete with the listen and server_name directives, the root vs alias difference, index, and error_page.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

One NGINX server can serve many websites at the same time. This concept is called a virtual host or server block. This Episode 3 teaches you how to build a static web server and separate several different domains within a single NGINX installation.

By the end of this episode, you'll be able to build a static web file structure, define multiple server blocks for different domains, understand when to use root and when to use alias, and handle custom error pages. All examples are run directly on a real server.

The Server Block Concept

One NGINX, Many Websites

A server block is a server context inside http. Each block defines how NGINX responds to requests for a specific combination of port, IP, and domain name. When a request arrives, NGINX matches it to the most appropriate server block based on the listen and server_name directives.

The location for server block files on Ubuntu is typically /etc/nginx/sites-available/, with a symlink created to /etc/nginx/sites-enabled/. On other distributions like RHEL, just put the file in /etc/nginx/conf.d/.

The listen Directive

IPv4 Port, IPv6 Port, and default_server

The listen directive determines the address and port a server block listens on:

Variants of the listen directive
server {
    listen 80;
    listen [::]:80;
}
 
server {
    listen 443 ssl default_server;
    server_name _;
}

listen 80; listens on IPv4, listen [::]:80; listens on IPv6. The default_server parameter marks the server block as the fallback destination when no server_name matches. The key point: there can only be one default_server per address and port combination.

The server_name Directive

Exact, Wildcard, and Regex

server_name determines which domain the block serves. There are three patterns:

  • Exact match: server_name example.com; — matches the host name exactly.
  • Wildcard: server_name *.example.com; — matches subdomains like api.example.com.
  • Regex: server_name ~^(?<user>.+)\.example\.net$; — complex patterns with named captures.
Server block with multiple server_name values
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example;
}

If you use a wildcard like *.example.com, make sure you also have a DNS record pointing *.example.com at the server's IP.

root vs alias

The Fundamental Difference

root and alias determine how the file path is combined with the URI, and the difference is often confusing:

  • root /var/www/site; — the URI is appended directly: a request for /blog/halo looks up /var/www/site/blog/halo.
  • alias /var/www/files; — the part of the URI that matched the location is replaced: location /assets/ with alias /var/www/files/ makes a request for /assets/x.png look up /var/www/files/x.png.
The difference between root and alias
server {
    listen 80;
    server_name example.com;
    root /var/www/site;
 
    location /assets/ {
        alias /var/www/assets/;
    }
}

The practical rule: use root at the server level for the document root, and alias when the directory on the server doesn't mirror the public URI structure.

The index and error_page Directives

Default Files

index determines which file NGINX looks for when a request points at a directory:

Setting up index
server {
    listen 80;
    server_name example.com;
    root /var/www/site;
    index index.html index.htm;
}

NGINX will try index.html first, then index.htm.

Custom Error Pages

error_page points error requests at a specific file without changing the status code:

Custom error page
server {
    listen 80;
    server_name example.com;
    root /var/www/site;
    error_page 404 /404.html;
    location = /404.html {
        internal;
    }
}

Important requirement: the file used as an error_page target must be accessed internally. The internal; directive prevents users from accessing that file directly.

Building Two Complete Server Blocks

Two Domains, One Server

A complete example of two websites on one server. Create a directory for each domain, then write the configuration file:

First domain's server block
server {
    listen 80;
    server_name example.com;
    root /var/www/example;
    index index.html;
    error_page 404 /404.html;
}
 
server {
    listen 80;
    server_name blog.example.com;
    root /var/www/blog;
    index index.html;
    error_page 404 /404.html;
}

Save it at /etc/nginx/conf.d/example.com.conf, test with nginx -t, then reload:

Enable the new configuration
sudo nginx -t
sudo nginx -s reload

Finally, verify that each domain responds with the correct content.

Conclusion

Episode 3 completed the static web server foundation: you can define multiple server blocks in one NGINX, choose a block based on listen and server_name, distinguish root from alias, and configure index and error_page.

Key takeaways:

  • A server block is a virtual host: one NGINX for many domains.
  • listen 80 default_server; marks the fallback block for an address and port.
  • server_name can be exact, wildcard, or regex.
  • root appends the URI to the path; alias replaces the location prefix.
  • index sets the default file, error_page handles error pages.
  • Always test the configuration with nginx -t before reloading.

In the next episode we'll deep dive into location block matching and priority rules — the five location syntax types from exact match to prefix, and NGINX's priority order for picking the most appropriate location for a URI.