Nginx Virtual Hosts: Hosting Multiple Websites on One Server

Unlock the power of Nginx virtual hosts (server blocks) to efficiently host multiple websites or subdomains on a single server. This guide provides a comprehensive, step-by-step tutorial, covering directory setup, configuration file creation, enabling server blocks, and Nginx testing. Learn best practices for subdomains, default server blocks, HTTPS integration, and dedicated logging. Practical examples and essential troubleshooting tips will help you master multi-site Nginx hosting, optimizing resource usage and streamlining web server management.

36 views

Nginx Virtual Hosts: Hosting Multiple Websites on One Server

Modern web infrastructure often requires the ability to serve multiple websites or web applications from a single server instance. This not only optimizes resource utilization but also simplifies management and reduces operational costs. Nginx, known for its high performance, stability, rich feature set, and low resource consumption, achieves this through what it calls server blocks, often referred to as virtual hosts in the Apache world.

This comprehensive guide will walk you through the process of setting up Nginx virtual hosts to effectively manage and serve multiple distinct domain names or subdomains from a single Nginx server. Whether you're hosting example.com and anothersite.org, or a main site with subdomains like blog.example.com and shop.example.com, mastering Nginx server blocks is a fundamental skill for any system administrator or developer. By the end of this article, you'll have a clear understanding and practical examples to configure your Nginx server for multi-site hosting.

Understanding Nginx Server Blocks (Virtual Hosts)

At its core, an Nginx server block is a configuration directive defined within the Nginx configuration file (nginx.conf or included files). Each server block defines the configuration for a specific virtual host, dictating how Nginx should respond to requests for a particular domain or set of domains. Nginx uses the listen directive to specify the IP address and port it should listen on, and the server_name directive to identify which domain names or hostnames this server block should respond to.

When a request comes in, Nginx examines the Host header of the HTTP request and compares it against the server_name directives of its configured server blocks. It then serves the content defined in the matching server block. If no server_name matches, Nginx typically falls back to the default server block (the first server block or one explicitly marked as default_server).

Prerequisites

Before you begin, ensure you have the following:

  1. Nginx Installed: Nginx should be installed and running on your server. If not, you can usually install it via your system's package manager (e.g., sudo apt update && sudo apt install nginx on Ubuntu/Debian, sudo yum install nginx on CentOS/RHEL).
  2. Domain Names: You need at least two domain names (e.g., example1.com and example2.com) or subdomains (e.g., blog.example.com and app.example.com) that you want to host. These domains' DNS A/AAAA records must point to your server's public IP address.
  3. Basic Directory Structure: A plan for where your website files will reside. A common practice is /var/www/yourdomain.com/html.
  4. Sudo Privileges: You'll need sudo access to modify Nginx configuration files.

Step-by-Step Setup Guide

Let's set up two virtual hosts: example1.com and example2.com.

Step 1: Create Directory Structure for Websites

First, create root directories for each of your websites. This is where their HTML, CSS, JavaScript, and other static files will be stored. A common location is /var/www/.

sudo mkdir -p /var/www/example1.com/html
sudo mkdir -p /var/www/example2.com/html

# Set ownership to your user (replace $USER with your username) to allow editing
sudo chown -R $USER:$USER /var/www/example1.com/html
sudo chown -R $USER:$USER /var/www/example2.com/html

# Set read permissions for the web server
sudo chmod -R 755 /var/www

Next, create a simple index.html file in each directory to test the setup:

For /var/www/example1.com/html/index.html:

<!-- /var/www/example1.com/html/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Welcome to Example1.com!</title>
</head>
<body>
    <h1>Success! This is Example1.com.</h1>
    <p>This virtual host is working correctly.</p>
</body>
</html>

For /var/www/example2.com/html/index.html:

<!-- /var/www/example2.com/html/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Welcome to Example2.com!</title>
</head>
<body>
    <h1>Success! This is Example2.com.</h1>
    <p>This virtual host is also working!</p>
</body>
</html>

Step 2: Create Nginx Server Block Configuration Files

Nginx typically loads server block configurations from files in the /etc/nginx/sites-enabled/ directory. These files are usually symlinks to configurations stored in /etc/nginx/sites-available/. This separation allows you to store configurations that are not yet active or to easily enable/disable sites.

Create a new configuration file for example1.com:

sudo nano /etc/nginx/sites-available/example1.com.conf

Add the following content:

# /etc/nginx/sites-available/example1.com.conf
server {
    listen 80;
    listen [::]:80;

    root /var/www/example1.com/html;
    index index.html index.htm index.nginx-debian.html;

    server_name example1.com www.example1.com;

    location / {
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/example1.com_access.log;
    error_log /var/log/nginx/example1.com_error.log;
}

Explanation of Directives:

  • listen 80;: Nginx listens on port 80 (standard HTTP). listen [::]:80; is for IPv6.
  • root /var/www/example1.com/html;: Specifies the document root for this server block. Nginx will look for files within this directory.
  • index index.html ...;: Defines the default file Nginx should serve when a directory is requested (e.g., when someone visits example1.com/).
  • server_name example1.com www.example1.com;: This is crucial. It tells Nginx to respond to requests for example1.com or www.example1.com using this server block's configuration.
  • location / { ... }: A block defining how to handle requests for specific URIs. try_files attempts to serve a file directly ($uri), then a directory ($uri/), and finally returns a 404 Not Found error.
  • access_log and error_log: Specifies separate log files for this specific site, which is a good practice for easier debugging and analytics.

Now, create a similar configuration file for example2.com:

sudo nano /etc/nginx/sites-available/example2.com.conf

Add the following content:

# /etc/nginx/sites-available/example2.com.conf
server {
    listen 80;
    listen [::]:80;

    root /var/www/example2.com/html;
    index index.html index.htm index.nginx-debian.html;

    server_name example2.com www.example2.com;

    location / {
        try_files $uri $uri/ =404;
    }

    access_log /var/log/nginx/example2.com_access.log;
    error_log /var/log/nginx/example2.com_error.log;
}

Step 3: Enable Server Blocks

To enable these configurations, create symbolic links from the sites-available directory to the sites-enabled directory. This tells Nginx to include these files when it starts up.

sudo ln -s /etc/nginx/sites-available/example1.com.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/example2.com.conf /etc/nginx/sites-enabled/

Step 4: Test Nginx Configuration

It's crucial to test your Nginx configuration for syntax errors before reloading. This prevents Nginx from failing to restart due to a typo.

sudo nginx -t

You should see output similar to this, indicating success:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If you see any errors, fix them in the respective configuration files and re-run sudo nginx -t until it passes.

Step 5: Restart Nginx

Apply the new configuration by restarting or reloading Nginx. reload is generally preferred as it allows Nginx to load new configurations without dropping active connections.

sudo systemctl reload nginx
# Or, if reload doesn't work or for fresh installations:
sudo systemctl restart nginx

Step 6: Update DNS Records

Ensure that the DNS A records for example1.com, www.example1.com, example2.com, and www.example2.com all point to the IP address of your Nginx server. Without correct DNS entries, your browser won't know where to find your websites.

Once DNS propagation completes (which can take a few minutes to several hours), you should be able to visit http://example1.com and http://example2.com in your web browser and see the respective index.html pages.

Advanced Scenarios and Best Practices

Hosting Subdomains

Hosting subdomains (e.g., blog.example.com, shop.example.com) works exactly like hosting separate domains. You just define a new server block with the subdomain as the server_name.

Example for blog.example.com:

# /etc/nginx/sites-available/blog.example.com.conf
server {
    listen 80;
    listen [::]:80;

    root /var/www/blog.example.com/html;
    index index.html;

    server_name blog.example.com;

    location / {
        try_files $uri $uri/ =404;
    }
}

Remember to create the directory (/var/www/blog.example.com/html), create an index.html, create the symlink, and reload Nginx.

The Default Server Block

It's good practice to have a default server block that catches requests for domain names that don't match any other server_name directive on your server. This prevents unknown requests from being served by the "first" virtual host Nginx finds, or allows you to serve a generic "site not found" page.

Typically, the first server block in your nginx.conf or sites-enabled is implicitly the default. You can explicitly set one using default_server:

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name _;
    # The underscore `_` is a non-existent domain name that will never match a real request.
    # You can also use localhost.

    root /var/www/default_site/html;
    index index.html;

    location / {
        return 444; # Return a Nginx-specific 444 error (no response) for unknown hosts
        # Or, serve a generic landing page:
        # try_files $uri $uri/ =404;
    }
}

Warning: If you define a default_server block, make sure only one server block on a given listen port has the default_server flag, otherwise Nginx will log a warning.

Securing Virtual Hosts with HTTPS (SSL/TLS)

For production websites, enabling HTTPS is essential. This involves obtaining an SSL/TLS certificate (e.g., via Let's Encrypt using Certbot) and configuring Nginx to listen on port 443 with the certificate.

A typical HTTPS server block looks like this (after obtaining certificates):

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name example1.com www.example1.com;

    root /var/www/example1.com/html;
    index index.html;

    ssl_certificate /etc/letsencrypt/live/example1.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example1.com/privkey.pem;

    # Include other SSL configurations (ciphers, protocols, etc.)
    include /etc/nginx/snippets/ssl-params.conf;
    include /etc/nginx/snippets/force-ssl.conf; # Optional: redirects HTTP to HTTPS

    location / {
        try_files $uri $uri/ =404;
    }
}

# Optional: HTTP to HTTPS redirect for this domain
server {
    listen 80;
    listen [::]:80;
    server_name example1.com www.example1.com;
    return 301 https://$host$request_uri;
}

It's common to have a separate HTTP server block whose sole purpose is to redirect all traffic to its HTTPS counterpart.

Logging for Each Site

As shown in the examples, dedicating separate access_log and error_log files for each virtual host is a best practice. This makes it significantly easier to debug issues and analyze traffic for individual websites without sifting through combined logs.

Configuration File Structure

For larger deployments, consider organizing your Nginx configuration files like this:

  • nginx.conf: Main configuration, includes conf.d/*.conf and sites-enabled/*.
  • conf.d/: General server-wide settings (e.g., Gzip, caching).
  • snippets/: Reusable Nginx configuration snippets (e.g., SSL parameters, common location blocks).
  • sites-available/: Individual server blocks for each website.
  • sites-enabled/: Symbolic links to active configurations in sites-available/.

Troubleshooting Common Issues

  • 403 Forbidden Error: This usually means Nginx doesn't have read access to your website's files or directories. Double-check file and directory permissions (e.g., sudo chmod -R 755 /var/www/yourdomain.com/html and ensure the Nginx user, typically www-data or nginx, can read them).
  • 404 Not Found Error: Verify that the root directive in your server block points to the correct directory and that your index.html file exists in that location. Also, ensure try_files is correctly configured.
  • Wrong Site is Loading: This often indicates an issue with the server_name directive. Ensure the server_name exactly matches the domain name you're trying to access (including www. or subdomains). Also, check your DNS records.
  • Nginx Fails to Start/Reload: Always use sudo nginx -t to test your configuration before attempting to reload or restart Nginx. Error messages will pinpoint the line and file where the syntax error occurred.
  • DNS Issues: If you can access your site by IP address but not by domain name, it's almost certainly a DNS problem. Use dig or nslookup to verify your domain's A records point to the correct server IP.

Conclusion

Nginx virtual hosts (server blocks) provide a powerful and flexible way to host multiple websites on a single server. By correctly configuring server blocks with appropriate listen, server_name, root, and location directives, you can efficiently manage diverse web properties. This approach not only conserves resources but also centralizes server administration.

With the foundational knowledge and practical steps outlined in this guide, you are now equipped to set up and manage multiple domains on your Nginx server. Remember to always test your configurations, secure your sites with HTTPS, and follow best practices for logging and directory structure for a robust and maintainable web environment. From here, you can further explore Nginx's capabilities like reverse proxying, load balancing, and caching to enhance your web server's performance and reliability.