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.
| Setting | Value |
|---|---|
| Server | nginx/1.27 (Alpine) |
server_name | nginx-a.srv1957161.hstgr.cloud |
root | /srv/site-a |
| This file | /srv/site-a/index.html |
autoindex | off (no directory listings) |
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.
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.
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