Nginx 1.27 · vhost A

You are on Nginx site A

Your request arrived with Host: nginx-a.srv1957161.hstgr.cloud. Nginx matched that against the server_name of each server block, picked this one, and served index.html from its root. Check the response headers — this vhost stamps X-Lab-Vhost: nginx site-a on everything it serves, so you can prove which block answered.

What decided this response

SettingValue
Servernginx/1.27 (Alpine)
server_namenginx-a.srv1957161.hstgr.cloud
root/srv/site-a
This file/srv/site-a/index.html
autoindexoff (no directory listings)

The config block behind this page

server {
    listen 80;
    server_name nginx-a.srv1957161.hstgr.cloud;

    root  /srv/site-a;
    index index.html;
    autoindex off;

    add_header X-Lab-Vhost "nginx site-a" always;

    # deliberate 403 -- file exists, config refuses
    location /forbidden/ {
        deny all;
    }
}

Note what is not here: no <VirtualHost> wrapper, no <Directory> blocks. Nginx uses root instead of DocumentRoot, and location instead of <Directory> — and location matches request URIs, not filesystem paths. That distinction is the source of most Apache-to-Nginx porting mistakes.

Try the failure modes

403 Forbidden → 404 Not Found → Same server, vhost B →

The 403 path holds a real, readable file; deny all is what stops it. The 404 means the opposite — correct vhost, correct root, the file simply is not there. Distinguishing those two is most of virtual host debugging.

Confirm from a shell

curl -sI https://nginx-a.srv1957161.hstgr.cloud/ | grep -i x-lab-vhost
curl -s -o /dev/null -w '%{http_code}\n' https://nginx-a.srv1957161.hstgr.cloud/forbidden/
curl -s -o /dev/null -w '%{http_code}\n' https://nginx-a.srv1957161.hstgr.cloud/nope

# on the server itself:
nginx -t     # syntax check -- always before a reload
nginx -T     # dump the entire merged config, every server block
nginx -s reload