How to host several websites on one VPS with nginx server blocks
One VPS can serve many websites. Each visitor's browser tells the server which domain it wants, and nginx uses that to pick the right site. The mechanism is called a server block, and once you understand it, adding a new site takes a few minutes and a short file.
How nginx chooses
When a request arrives, nginx looks at the Host header the browser sent, such as shop.mydomain.net, and compares it with the server_name values in your server blocks. The matching block decides what to do: serve files from a folder, or pass the request to an application. If nothing matches, nginx uses the default server.
All your sites share the same IP address and the same ports, 80 and 443. The domain name is what separates them. Each domain needs a DNS record pointing to the server. See Point your domain to your server and DNS records explained.
Where the files go
On Ubuntu and Debian, nginx uses two folders:
/etc/nginx/sites-available/holds a config file for each site. Nothing here is active on its own./etc/nginx/sites-enabled/holds links to the files insites-availablethat you want active.
That arrangement lets you switch a site off by removing the link, without deleting the file.
Add a static site
- Make a folder for the site's files, and a test page:
sudo mkdir -p /var/www/site-one
echo "Hello from site one" | sudo tee /var/www/site-one/index.html
- Create the config, for example
/etc/nginx/sites-available/site-one:
server {
listen 80;
listen [::]:80;
server_name site-one.net www.site-one.net;
root /var/www/site-one;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
- Enable it with a link, test the syntax and reload:
sudo ln -s /etc/nginx/sites-available/site-one /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx -t checks the configuration before you apply it. Never skip it. A typo can take every site down.
Add a second site
Repeat the process with a different folder, file name and server_name. That is all. Both sites now respond on the same server, each to its own domain.
Pass a site to an application
For a Node.js, Python or container app, the block forwards requests instead of serving files:
server {
listen 80;
server_name app.mydomain.net;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
More detail, including keeping the app running, is in Run a Node.js app on your VPS with PM2 and nginx.
Add HTTPS
Use Certbot, which edits the right server blocks for you. For each domain:
sudo certbot --nginx -d site-one.net -d www.site-one.net
It obtains a certificate and adds the HTTPS settings and a redirect. See Set up an SSL certificate (HTTPS). If a renewal ever fails, Fixing Let's Encrypt renewal problems helps.
A default server for unknown names
By default, a request for an unknown domain pointed at your IP will land on the first server block, which may expose one of your sites under a wrong name. Add an explicit catch-all that does nothing useful:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
Status 444 is nginx-specific, and means "close the connection without answering".
Logs
Each site can have its own logs, which makes troubleshooting far easier:
access_log /var/log/nginx/site-one.access.log;
error_log /var/log/nginx/site-one.error.log;
When something is wrong, the error log is the first thing to read. How to read your server logs has tips on finding the real cause.
Common mistakes
| Mistake | Effect |
|---|---|
| Missing semicolon at the end of a line | nginx -t reports a syntax error |
Same server_name in two files | A "conflicting server name" warning. One block is ignored |
Forgetting the link in sites-enabled | The site does not exist as far as nginx is concerned |
| Wrong folder permissions | 403 Forbidden. The www-data user must be able to read the files |
| Not reloading after a change | The old configuration keeps running |
| DNS not pointing at the server | The domain never reaches nginx |
A conflicting name is easy to cause. If you copy a config to make a new site and forget to change server_name, nginx warns about it and silently uses one of the two.
When a page returns an error
A 502 or 504 means nginx could not get a good answer from the application behind it. See How to fix nginx 502 and 504 errors.
Still stuck? Open a ticket on Discord - run /ticket in any channel, pick a department, and include your Server ID or Invoice ID so we can jump straight in.