When running multiple websites on a single Nginx server, you'll typically configure multiple server blocks (virtual hosts). But what happens when a request comes in that doesn't match any of your defined server_name directives? Maybe it's:
- A direct IP address access
- A mistyped domain name
- An old domain that used to point to your server
- A malicious scan probing your server
By default, Nginx will use the first server block it finds as the default catch-all. This is often not what you want, especially if your first vhost is for a production website.
The proper way is to define an explicit default server block that will catch all unhandled requests:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# For HTTPS
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
# ssl_certificate /path/to/default/cert;
# ssl_certificate_key /path/to/default/key;
return 444; # Close connection silently
# Alternative options:
# return 403; # Forbid access
# rewrite ^ https://yourmaindomain.com$request_uri permanent; # Redirect to primary domain
}
Here are some common approaches for the default server block:
Option 1: Silent Connection Closing (444)
The Nginx-specific 444 code immediately closes the connection:
return 444;
Option 2: Redirect to Primary Domain
return 301 https://yourmaindomain.com$request_uri;
Option 3: Custom Error Page
root /var/www/default;
index index.html;
location / {
try_files $uri $uri/ =404;
}
Here's how this looks in a real configuration with multiple vhosts:
# Default catch-all (must come first)
server {
listen 80 default_server;
server_name _;
return 444;
}
# Production website
server {
listen 80;
server_name example.com www.example.com;
# ... rest of config ...
}
# Development site
server {
listen 80;
server_name dev.example.com;
# ... rest of config ...
}
After implementing, test with:
curl -v http://your.server.ip
curl -v http://nonexistent-domain.test
When dealing with multiple virtual hosts in Nginx, you might encounter situations where requests come in that don't match any configured server_name. These could be:
- Direct IP access to your server
- Domains pointing to your server but not configured
- Typos in domain names
- Legacy domains no longer in use
Nginx processes server blocks in the order they're loaded, and the first server block becomes the default for unmatched requests. Here's the proper way to set this up:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# For HTTPS (if applicable)
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
# Basic logging for debugging
access_log /var/log/nginx/default_access.log;
error_log /var/log/nginx/default_error.log;
# You can choose to:
# 1. Return 444 (Nginx-specific "close connection")
# 2. Redirect to your main domain
# 3. Show a custom maintenance page
# Option 1: Close connection silently
return 444;
# Option 2: Redirect to primary domain
# return 301 https://yourmaindomain.com$request_uri;
# Option 3: Serve a custom page
# root /var/www/default;
# index index.html;
# try_files $uri /index.html =404;
}
Key elements to remember when setting up your default server block:
- The
default_server
parameter must be specified in the listen directive server_name _;
is a catch-all pattern- Place this configuration before other server blocks in your config files
- For HTTPS, you'll need a wildcard or default SSL certificate
For more complex setups, you might want to handle different scenarios:
# /etc/nginx/conf.d/default.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# Detect if request came via HTTPS but hit this HTTP block
if ($http_x_forwarded_proto = "https") {
return 301 https://$host$request_uri;
}
# Handle known bots differently
if ($http_user_agent ~* (bot|crawl|spider)) {
root /var/www/bot_trap;
try_files $uri /bot_message.html =404;
}
# All other cases
return 301 https://yourmaindomain.com$request_uri;
}
After implementing, test with these commands:
sudo nginx -t
sudo systemctl reload nginx
Then verify using:
curl -I http://your.server.ip
curl -H "Host: randomdomain.com" http://your.server.ip