When running multiple domains on a single Nginx instance, you might encounter an unexpected behavior: Nginx serves content from your primary domain even when requests hit undefined hostnames. This occurs because Nginx automatically uses the first server
block as the default when no matching server_name
is found.
To properly handle undefined domains, we need to create a catch-all server block that explicitly returns 404. Here's the correct implementation:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 404;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_certificate /path/to/dummy.crt;
ssl_certificate_key /path/to/dummy.key;
return 404;
}
server {
listen 80;
server_name web.example.com;
# Your web configuration
}
server {
listen 443 ssl;
server_name web.example.com;
ssl_certificate /path/to/web.crt;
ssl_certificate_key /path/to/web.key;
# Your web configuration
}
- The
default_server
parameter forces Nginx to use this block for unmatched requests server_name _
is a special wildcard that matches any hostname- For HTTPS, you must provide dummy certificates (self-signed works)
- Place default blocks before other server declarations
Verify your setup works by making requests to undefined domains:
curl -I http://random.example.com
curl -Ik https://random.example.com
Both should return HTTP 404 status codes.
Some administrators prefer redirecting unknown hosts instead of showing 404s:
server {
listen 80 default_server;
server_name _;
return 301 https://web.example.com$request_uri;
}
For security and SEO:
- Always configure a default server
- Consider logging invalid host access attempts
- Regularly review your SSL/TLS configuration
- Monitor for potential DNS spoofing attempts
When running multiple domains on a single Nginx instance, you might encounter this scenario:
# Current problematic config:
server {
listen 80;
server_name web.example.com;
# web configuration
}
server {
listen 443;
server_name web.example.com;
# HTTPS configuration
}
Despite only defining web.example.com
, requests to home.example.com
(pointing to the same IP) still get served by the web
configuration. This happens because Nginx defaults to using the first defined server block when no match is found.
Nginx evaluates server blocks in this order:
- Exact
server_name
match - Wildcard match (e.g., *.example.com)
- Regular expression match
- Default server (first defined or explicitly marked)
To properly handle unknown hostnames, we need to:
# Correct configuration example
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _; # Catch-all invalid hostnames
ssl_certificate /path/to/dummy.crt;
ssl_certificate_key /path/to/dummy.key;
return 404; # Or 444 to close connection silently
}
server {
listen 80;
server_name web.example.com;
# web HTTP configuration
}
server {
listen 443 ssl;
server_name web.example.com;
# web HTTPS configuration
}
For HTTPS traffic, you'll need dummy certificates for the default server. Here's how to generate them:
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/dummy.key \
-out /etc/nginx/ssl/dummy.crt \
-subj "/CN=invalid"
If you prefer handling HTTP and HTTPS differently:
# HTTP-only default server
server {
listen 80 default_server;
server_name _;
return 444; # Close connection immediately
}
# HTTPS default server
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /path/to/dummy.crt;
ssl_certificate_key /path/to/dummy.key;
return 403; # Or other appropriate status
}
After making changes, always verify:
nginx -t
systemctl reload nginx
Test with these commands:
curl -I http://unknown.example.com
curl -Ik https://unknown.example.com