Websites & apps

Host multiple websites on one VPS with Nginx server blocks

Run several websites on one Linux VPS by giving each domain its own Nginx server block, its own web root and its own logs, then test and reload safely.

BeginnerLinux server5 min read

One Linux VPS can serve many websites. Nginx decides which site to show by reading the domain name the browser asks for and matching it to a server block, which is Nginx's name for a virtual host. This guide sets up two sites, example.com and example.net, each with its own folder, config file and logs.

Before you start

  • A Linux VPS running Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux/Rocky Linux 8 or 9.
  • Nginx installed and running. If it is not, follow Install Nginx on Linux first.
  • A sudo user and an SSH session to the server.
  • DNS A records for each domain (and its www name) pointing at your server's IP, for example 203.0.113.10. See Point a domain to your VPS.

How Nginx picks a site

When a request arrives, Nginx looks at the port it came in on and the Host header the browser sent. It then compares that host with the server_name of every server block listening on that port. The block that matches serves the request.

If nothing matches, Nginx uses the default server for that port. That is why a fresh install shows the "Welcome to nginx!" page for any domain you point at it.

Where server blocks live depends on the distribution:

  • Ubuntu and Debian: files in /etc/nginx/sites-available/, switched on with a symlink in /etc/nginx/sites-enabled/.
  • AlmaLinux and Rocky Linux: any file ending in .conf inside /etc/nginx/conf.d/ is loaded automatically.

Create a folder for each site

Give every site its own web root. Keeping them under /var/www makes backups and permissions simple.

Bash
sudo mkdir -p /var/www/example.com/public /var/www/example.net/public

Add a test page to each so you can tell them apart:

Bash
echo '<h1>example.com works</h1>' | sudo tee /var/www/example.com/public/index.html
echo '<h1>example.net works</h1>' | sudo tee /var/www/example.net/public/index.html

Set ownership so your deploy user can upload files and Nginx can read them:

Bash
sudo chown -R deploy:deploy /var/www/example.com /var/www/example.net
sudo chmod -R 755 /var/www/example.com /var/www/example.net

Replace deploy with your own username.

Write the server block

On Ubuntu or Debian, create /etc/nginx/sites-available/example.com. On AlmaLinux or Rocky Linux, create /etc/nginx/conf.d/example.com.conf instead.

Bash
sudo nano /etc/nginx/sites-available/example.com

Paste this:

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

    server_name example.com www.example.com;

    root /var/www/example.com/public;
    index index.html index.htm index.php;

    access_log /var/log/nginx/example.com.access.log;
    error_log  /var/log/nginx/example.com.error.log;

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

What each part does:

  • listen 80 and listen [::]:80 accept plain HTTP on IPv4 and IPv6.
  • server_name lists every hostname this site answers to. Separate names with spaces.
  • root is the folder Nginx serves files from.
  • try_files returns the file if it exists, then a folder, then a 404.
  • Separate log files make it easy to debug one site without wading through the others.

Repeat the file for the second site, changing every example.com to example.net.

Note: Two server blocks must not list the same name in server_name. Nginx warns about a "conflicting server name" and ignores the second one.

Enable the sites

On Ubuntu and Debian, link each file into sites-enabled:

Bash
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/example.net /etc/nginx/sites-enabled/

To switch a site off later, delete only the link in sites-enabled. The original stays in sites-available.

On AlmaLinux and Rocky Linux there is no extra step. Files in conf.d ending in .conf are loaded automatically. On those systems, also make sure SELinux allows Nginx to read your web root:

Bash
sudo restorecon -Rv /var/www

Test and reload

Always check the syntax before applying a change:

Bash
sudo nginx -t

You want both lines to say ok and successful:

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

Then reload. A reload keeps current connections open and loads the new config:

Bash
sudo systemctl reload nginx

If nginx -t reports an error, it names the file and line. Fix it and run the test again before reloading. More on this in Test and reload Nginx config without downtime.

Check it works

Ask Nginx for each site by name, from the server itself. This works even before DNS has updated:

Bash
curl -H "Host: example.com" http://127.0.0.1/
curl -H "Host: example.net" http://127.0.0.1/

Each should print its own test heading. Once DNS points at the server, open both domains in a browser. To see which config files Nginx actually loaded, list every server_name it knows:

Bash
sudo nginx -T | grep -E "server_name|# configuration file"

Add HTTPS

Each site needs its own certificate, or one certificate that covers all its names. With Certbot you can request one per site:

Bash
sudo certbot --nginx -d example.com -d www.example.com

Certbot edits the matching server block and adds the SSL settings. The full walkthrough is in Free SSL for Nginx with Let's Encrypt.

Troubleshooting

  • The welcome page appears instead of your site: the server_name does not match the domain you typed, or the site is not enabled. Check the symlink and see Fix Nginx showing the default page.
  • Both domains show the same site: one domain is missing from every server_name, so it falls through to the default server. Add it to the right block and reload.
  • 403 Forbidden: there is no index.html in the root, or Nginx cannot read the folder. Check permissions with namei -l /var/www/example.com/public/index.html.
  • nginx -t says "conflicting server name": the same hostname appears in two files. Remove it from one.
  • Site works by IP but not by name: DNS is not pointing at the server yet. Check with dig +short example.com.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.