HomeWebGuides → default_server

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.

Found while decommissioning a subdomain · August 2026 · by SPELL
1stVhost loaded wins
A–Zsites-enabled order
410What to serve instead
0Warnings you get

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:


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.

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 →

More on this