Delete a vhost,
serve someone else's site.
If no server block is marked default_server, nginx picks one for you — the first it loads, which on Debian and Ubuntu means alphabetically first from sites-enabled. Every hostname that does not match a vhost gets that site. Including hostnames you just removed.
How we found it
We were removing a subdomain — a leftover build copy of a client site that had become indexable and was taking about a tenth of the domain's search impressions. The obvious move is to delete the vhost and the files.
Before doing it we checked what an unmatched hostname currently receives:
curl -sk -H "Host: nope.example.com" \
https://127.0.0.1/ \
| grep -oE '<title>[^<]*'
It returned the webmail login page. No default_server was declared anywhere,
so nginx had silently elected mail.example.com — first alphabetically in
sites-enabled — as the default for every unmatched request.
What deleting the vhost would have done
The subdomain would not have stopped resolving — DNS still pointed at the box. It would have started serving the webmail login, on a hostname search engines had already indexed. A tidy-up would have published a login page under a URL with existing crawl history.
The rule, precisely
For each listening address and port, nginx picks the server block whose server_name matches
the Host header. If none matches, it uses the default server for that address:port — the one
marked default_server, or, if none is marked, the first one nginx loaded.
Load order is the order of include directives. On Debian and Ubuntu that is
include /etc/nginx/sites-enabled/*;, which the shell expands alphabetically. So
your default site is whichever config file sorts first — a fact no one chose and nothing warns you about.
Two consequences worth sitting with:
- Anyone who points a domain at your IP gets served one of your sites, under their hostname. That is a duplicate-content problem you did not create and will not be told about.
- Renaming a config file can silently change which site is the default.
Check yours in two commands
grep -rn "default_server" \
/etc/nginx/nginx.conf /etc/nginx/sites-enabled/
ls -1 /etc/nginx/sites-enabled/
If the first prints nothing, the second line tells you your answer: whatever sorts first is your default site. Confirm it the way we did, with a Host header for a name you do not serve.
The fix — a catch-all that refuses
Add an explicit default that answers nothing useful. 444 closes the connection without a
response, which is the right answer for junk traffic.
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
HTTPS needs more care: to close a TLS connection you must first complete a handshake, so the block needs a certificate. Reuse any certificate you already hold — clients will get a name mismatch, which is correct, because they asked for a name you do not serve.
# reuse any cert you already hold — a name
# mismatch here is correct
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/\
example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/\
example.com/privkey.pem;
return 444;
}
Retiring a hostname properly
A catch-all handles the unknown. A hostname you are deliberately removing deserves better than falling into it, especially if search engines know about it.
- Serve 410, not 404410 Gone means permanently removed and de-indexes faster than 404, which only means "not here right now".
- Keep the certificateCounter-intuitive but important: without valid TLS, Googlebot gets a handshake error instead of your 410, and the old URLs linger. Keep the cert until the URLs have dropped, then delete it.
- Add noindex too
add_header X-Robots-Tag "noindex, nofollow" always;— belt and braces while the 410s are being picked up. - Remove DNS lastIf DNS stops resolving first, crawlers cannot fetch the 410 that tells them it is gone.
And always nginx -t before reloading. On a box with several sites on it, a bad reload takes
every tenant down — not only the one you were working on.
We run multi-tenant production on this stack
Server configuration, TLS, vhost hygiene and the checks that catch this class of problem before it ships. Tell us what you are running and we will tell you what it is currently doing.
Book a call →