Configuration Essentials

Virtual hosts, document roots, MIME types, and the declarative model

The Declarative Model

Web server configuration is declarative: you describe what you want to happen, not how to do it. You say "serve files from this directory" or "proxy requests to this backend"—the server figures out the implementation details.

This differs from writing application code (imperative), where you specify exact steps. Understanding this distinction helps you write effective configurations: focus on the outcome, let the server handle the mechanics.

Configuration vs. Code: When you configure Nginx, you're not programming—you're describing rules. The server matches requests against your rules and takes the corresponding action. Think of it as pattern matching, not step-by-step instructions.

Virtual Hosts: One Server, Many Domains

A single server can host multiple websites. When a request arrives, the server checks the Host header to determine which site to serve:

Request:                           Server:
GET / HTTP/1.1                     ┌─────────────────────────────┐
Host: shop.example.com ─────────►  │  Which virtual host?        │
                                   │                             │
                                   │  shop.example.com → /shop/  │
                                   │  blog.example.com → /blog/  │
                                   │  api.example.com  → proxy   │
                                   └─────────────────────────────┘
				
Virtual hosting routes requests by domain

This is called name-based virtual hosting. A single IP address can serve hundreds of different websites, each with its own configuration.

# /etc/nginx/sites-available/shop.example.com server { listen 80; server_name shop.example.com; root /var/www/shop; index index.html; } # /etc/nginx/sites-available/blog.example.com server { listen 80; server_name blog.example.com; root /var/www/blog; index index.html; }
# /etc/apache2/sites-available/shop.example.com.conf <VirtualHost *:80> ServerName shop.example.com DocumentRoot /var/www/shop DirectoryIndex index.html </VirtualHost> # /etc/apache2/sites-available/blog.example.com.conf <VirtualHost *:80> ServerName blog.example.com DocumentRoot /var/www/blog DirectoryIndex index.html </VirtualHost>
import { createServer } from 'node:http'; import { join } from 'node:path'; import { createReadStream } from 'node:fs'; const sites = { 'shop.example.com': '/var/www/shop', 'blog.example.com': '/var/www/blog' }; const server = createServer((req, res) => { const host = req.headers.host; const root = sites[host]; if (!root) { res.writeHead(404); res.end('Site not found'); return; } const path = req.url === '/' ? '/index.html' : req.url; const file = join(root, path); createReadStream(file).pipe(res); }); server.listen(80);

Notice how Nginx and Apache configurations are purely declarative—no procedural logic. Node.js, being a programming environment, requires you to implement the virtual host logic yourself.

Document Root and Directory Structure

The document root is the directory on the filesystem that maps to the root URL (/) of your site. When a client requests /images/logo.png, the server looks for {document_root}/images/logo.png.

# Document root mapping Request: GET /css/style.css Document root: /var/www/mysite Filesystem path: /var/www/mysite/css/style.css

Common Directory Conventions

Path Location Notes
/var/www/ Default web root (Debian/Ubuntu) Standard location for all sites
/usr/share/nginx/html/ Nginx default Used for the default welcome page
/home/user/public_html/ User home directories For shared hosting setups
/srv/www/ FHS-compliant location Preferred by some distributions

Typical Site Structure

/var/www/mysite/ ├── index.html # Default document ├── about.html ├── css/ │ └── style.css ├── js/ │ └── main.js ├── images/ │ ├── logo.png │ └── hero.jpg └── downloads/ └── brochure.pdf

MIME Types

When serving files, the server must tell the browser what type of content it's sending. This is the Content-Type header, based on MIME types (Multipurpose Internet Mail Extensions).

Extension MIME Type Category
.html text/html Document
.css text/css Stylesheet
.js application/javascript Script
.json application/json Data
.png image/png Image
.jpg image/jpeg Image
.svg image/svg+xml Image
.woff2 font/woff2 Font
.pdf application/pdf Document

Servers typically have a default MIME types file (/etc/mime.types) that maps extensions to content types. You can add custom mappings:

# Include standard MIME types include /etc/nginx/mime.types; # Add custom types types { application/wasm wasm; text/markdown md; } # Default for unknown extensions default_type application/octet-stream;
# Add custom MIME types AddType application/wasm .wasm AddType text/markdown .md # Default for unknown extensions DefaultType application/octet-stream

Why MIME types matter: Browsers use the Content-Type header to decide how to handle content. A JavaScript file served as text/plain won't execute. An SVG served without image/svg+xml may not render. Incorrect MIME types are a common source of subtle bugs.

Index Files and Directory Behavior

When a request targets a directory (like /products/), the server needs to decide what to return. Options include:

  1. Serve an index file — Look for index.html, index.php, etc.
  2. Show a directory listing — Display the contents of the directory
  3. Return an error — 403 Forbidden or 404 Not Found
server { # Try these files in order when a directory is requested index index.html index.htm; location / { # Try: exact file → directory with index → 404 try_files $uri $uri/ =404; } # Enable directory listing for a specific path location /downloads/ { autoindex on; autoindex_exact_size off; # Show human-readable sizes } }
# Set default index files DirectoryIndex index.html index.htm # Disable directory listing globally <Directory /var/www> Options -Indexes </Directory> # Enable for specific directory <Directory /var/www/mysite/downloads> Options +Indexes IndexOptions FancyIndexing </Directory>

Configuration File Organization

Both Nginx and Apache support splitting configuration across multiple files. This keeps configurations manageable and allows enabling/disabling sites easily.

Nginx Structure

/etc/nginx/ ├── nginx.conf # Main configuration ├── mime.types # MIME type mappings ├── sites-available/ # All site configs │ ├── default │ ├── shop.example.com │ └── blog.example.com ├── sites-enabled/ # Symlinks to active sites │ ├── default -> ../sites-available/default │ └── shop.example.com -> ../sites-available/shop.example.com └── snippets/ # Reusable config fragments └── ssl-params.conf

Apache Structure

/etc/apache2/ ├── apache2.conf # Main configuration ├── ports.conf # Port definitions ├── sites-available/ # All site configs │ ├── 000-default.conf │ └── shop.example.com.conf ├── sites-enabled/ # Symlinks to active sites │ └── 000-default.conf -> ../sites-available/000-default.conf ├── mods-available/ # Available modules └── mods-enabled/ # Active modules

Enable and disable sites using command-line tools:

# Nginx sudo ln -s /etc/nginx/sites-available/shop.example.com /etc/nginx/sites-enabled/ sudo nginx -t # Test configuration sudo systemctl reload nginx # Apache sudo a2ensite shop.example.com.conf sudo a2dissite 000-default.conf sudo apache2ctl configtest # Test configuration sudo systemctl reload apache2

Testing Configuration Changes

Always test configuration changes before reloading:

# Nginx: test syntax sudo nginx -t # nginx: the configuration file /etc/nginx/nginx.conf syntax is ok # nginx: configuration file /etc/nginx/nginx.conf test is successful # Apache: test syntax sudo apache2ctl configtest # Syntax OK # Reload (graceful - doesn't drop connections) sudo systemctl reload nginx sudo systemctl reload apache2

Reload vs. Restart: reload tells the server to re-read its configuration without dropping existing connections. restart stops and starts the server, terminating active connections. Always prefer reload in production.

What's Next

Now that you understand basic configuration concepts, the next tutorial covers URL handling: how servers match requests to locations, perform redirects, and rewrite URLs.