Add a default server block in nginx
/etc/nginx/conf.d/00-default.confserver { listen 80 default_server; listen [::]:80 default_server; return 444;}
server { listen 443 ssl default_server; listen [::]:443 ssl default_server; ssl_reject_handshake on;}sudo nginx -t && sudo systemctl reload nginxWhy it matters
Section titled “Why it matters”When a request arrives with a Host header that matches no server_name, nginx
has to answer with something. Without an explicit default_server, it uses
the first server block for that listen address — chosen by config load order,
which is alphabetical filename order on Debian and Ubuntu.
So the site that answers for every scanner, every stale DNS record and every attacker probing by IP is whichever vhost happens to sort first. That’s an accident, and it has consequences:
- Your certificate is presented to anyone, for any name. TLS handshake first,
Hostcheck later — so a scanner probing your IP gets your certificate, which names your domains. That’s free reconnaissance. - A stale DNS record keeps working. A domain that used to point at you, now owned by someone else, still resolves and still gets served your first vhost.
- Host-header confusion. Applications that build URLs from
Hostcan be induced to emit links pointing at an attacker’s domain — password reset emails being the classic.
An explicit default block makes the answer a deliberate one: nothing.
ssl_reject_handshake is the good part
Section titled “ssl_reject_handshake is the good part”ssl_reject_handshake on (nginx 1.19.4+) rejects the TLS handshake without
presenting a certificate at all. That is better than serving a self-signed cert
or a dummy vhost, because it means an IP scan learns nothing — no names, no
issuer, no expiry.
It needs no certificate configured, which also removes the usual chicken-and-egg of “what cert does the reject block use”.
return 444 on port 80 is nginx’s non-standard code meaning close the
connection with no response. No body, no headers, no version banner — and no
bandwidth spent on a scanner.
default_server is per listen address
Section titled “default_server is per listen address”Two things people get wrong:
- It’s a parameter of
listen, not a standalone directive, and it applies per address:port. Adefault_serveron80does nothing for443. You need both, and both IPv4 and IPv6. - Only one server block per address:port may carry it. A second one is a
config error and nginx refuses to start — which is exactly why you test with
nginx -tbefore reloading, since a pre-existing default in the packagedsites-enabled/defaultwill collide with yours.
Related
Section titled “Related”- Set security headers correctly — what your real vhosts should send.
- Hide the nginx version — the other reconnaissance surface.