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:
- 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 nginxon Ubuntu/Debian,sudo yum install nginxon CentOS/RHEL). - Domain Names: You need at least two domain names (e.g.,
example1.comandexample2.com) or subdomains (e.g.,blog.example.comandapp.example.com) that you want to host. These domains' DNS A/AAAA records must point to your server's public IP address. - Basic Directory Structure: A plan for where your website files will reside. A common practice is
/var/www/yourdomain.com/html. - Sudo Privileges: You'll need
sudoaccess 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 visitsexample1.com/).server_name example1.com www.example1.com;: This is crucial. It tells Nginx to respond to requests forexample1.comorwww.example1.comusing this server block's configuration.location / { ... }: A block defining how to handle requests for specific URIs.try_filesattempts to serve a file directly ($uri), then a directory ($uri/), and finally returns a404 Not Founderror.access_loganderror_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, includesconf.d/*.confandsites-enabled/*.conf.d/: General server-wide settings (e.g., Gzip, caching).snippets/: Reusable Nginx configuration snippets (e.g., SSL parameters, commonlocationblocks).sites-available/: Individualserverblocks for each website.sites-enabled/: Symbolic links to active configurations insites-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/htmland ensure the Nginx user, typicallywww-dataornginx, can read them). - 404 Not Found Error: Verify that the
rootdirective in your server block points to the correct directory and that yourindex.htmlfile exists in that location. Also, ensuretry_filesis correctly configured. - Wrong Site is Loading: This often indicates an issue with the
server_namedirective. Ensure theserver_nameexactly matches the domain name you're trying to access (includingwww.or subdomains). Also, check your DNS records. - Nginx Fails to Start/Reload: Always use
sudo nginx -tto 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
digornslookupto 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.