Safeguard
Security Guides•

nginx default server: catch unknown Host headers and SNI names

How nginx picks a server block, why the first one becomes the default, and how a catch-all with ssl_reject_handshake refuses names you do not own.

Safeguard Security Team
3 min read

If no server block in nginx is marked default_server, the first one listed for that address and port answers any request whose Host name does not match. That can serve a real application to arbitrary names. Add an explicit catch-all that refuses unknown hosts, and on TLS use ssl_reject_handshake.

How nginx chooses

The core module documentation says that default_server on a listen directive makes that block the default for its address and port pair. If no block has the parameter, the first server listed for that pair is the default.

For the name, server_name defaults to an empty string, which matches requests without a Host header. The search order is exact name, then the longest wildcard starting with an asterisk, then the longest wildcard ending with an asterisk, then the first matching regex. The underscore in server_name _; is only a conventional placeholder, not a special catch-all.

Why unknown hosts are a problem

  • Host header abuse. Applications that build links from Host can produce poisoned reset emails or cached pages if an unexpected name reaches them. See Django allowed hosts.
  • Exposure by IP. Scanners request the bare address. If the first server block is your main app or an admin tool, it answers.
  • Subdomain takeover aftermath. DNS for an old name that still points to your address will be served by whichever block is default.
  • Certificate leakage. On TLS, the default block's certificate is shown to anyone who connects with an unknown or missing SNI name. That reveals a hostname you may not want listed.

A catch-all for plain HTTP

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

The 444 code is non standard and closes the connection without a response, and its full description is in the nginx rewrite module documentation. If you prefer a standard status, use 404 or 421.

A catch-all for TLS

The ssl module documentation includes this example: a default server on 443 with ssl_reject_handshake on. With that setting, handshakes in that block are rejected, so a client that sends an unknown SNI name never receives a certificate.

server {
    listen 443 ssl default_server;
    ssl_reject_handshake on;
}

The directive is available since nginx 1.19.4. Then every real site gets its own block with its own listen and server_name.

Check it

curl -sk -o /dev/null -w '%{http_code}\n' https://YOUR.IP.ADDRESS/
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: unknown.invalid' http://YOUR.IP.ADDRESS/

You want a refused connection or error, not your site. Compare with what certificate transparency lists for your names, since logs show hostnames you issued certificates for.

Where Safeguard fits

Safeguard provides asset discovery, helping you notice hosts and names you did not plan to expose, and dependency scanning for the software behind them. Free scan: https://gold.safeguard.sh/scan.

Quick check

What happens if no nginx server block has default_server?

The first server block listed for that address and port pair becomes the default.

What does ssl_reject_handshake on do?

It rejects TLS handshakes in that server block, so unknown SNI names get no certificate.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.