Host multiple sites with Nginx

2 min read07 November 2026

One VPS can serve several domain names without an extra container or machine. Nginx separates sites using the request's Host header: each domain gets its own server block, document root and logs. This guide sets up two sites, then shows the checks that prevent the wrong content from being served.

One server block per site

Create a separate root for each site. Static files are enough here; a PHP or Node.js application plugs into the same root or sits behind a proxy.

Bash
sudo mkdir -p /var/www/site-a.fr /var/www/site-b.fr
sudo chown -R deploy:www-data /var/www/site-a.fr /var/www/site-b.fr
sudo chmod 750 /var/www/site-a.fr /var/www/site-b.fr

Put an index.html in each folder, then create /etc/nginx/sites-available/site-a.fr.conf:

Nginx
server {
    listen 80;
    listen [::]:80;
    server_name site-a.fr www.site-a.fr;

    root /var/www/site-a.fr;
    index index.html;

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

    access_log /var/log/nginx/site-a.fr.access.log;
    error_log  /var/log/nginx/site-a.fr.error.log;
}

listen [::]:80 is required to answer over IPv6; without it, Nginx only listens on IPv4. server_name accepts several names, but the first matching block wins. try_files returns a clean 404 instead of listing the directory.

Enable the site and reload

The Debian package reads sites-enabled, not sites-available. A symbolic link activates the site without duplicating the file.

Bash
sudo ln -s /etc/nginx/sites-available/site-a.fr.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

nginx -t validates syntax and paths before any reload; do not skip it. reload applies the configuration without dropping active connections. Repeat for the second site.

To stop an unknown domain from landing on the first site, remove the package's /etc/nginx/sites-enabled/default link, then keep an explicit default block:

Nginx
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

Code 444 closes the connection without a response. If you prefer to keep the default site, adapt it instead of adding a second default_server, which Nginx would reject.

Verify before going public

Test locally with a Host header, then over IPv6 and from outside:

Bash
curl -H "Host: site-a.fr" http://127.0.0.1/
curl -6 -H "Host: site-a.fr" http://[::1]/
dig +short AAAA site-a.fr

Compare the responses and read /var/log/nginx/site-a.fr.access.log. A 403 almost always means permissions or a missing index; a 404 means a misspelled root. Check that www-data can traverse every parent directory with namei -l /var/www/site-a.fr/index.html.

Common mistakes

  • Two default_server blocks on the same port: Nginx refuses to start.
  • A server_name containing https:// or a slash: that is not a URL, only the name.
  • DNS still points elsewhere: test with curl --resolve site-a.fr:80:YOUR_IPV6.
  • After an edit, systemctl restart hides a syntax error; use nginx -t then reload.

Once both domains return their own content over IPv4 and IPv6, add HTTPS site by site. Keep a copy of /etc/nginx before the next change.

Advertisement

Was this article helpful?

Read next