When working with multiple virtual hosts in Nginx, the default server block (catch-all) can become a security and resource management concern. The default vHost processes requests that don't match any defined server_name, which might expose unintended content or waste server resources.
Here are three production-tested approaches to handle this:
# Option 1: Return 444 (Nginx-specific non-standard code)
server {
listen 80 default_server;
listen [::]:80 default_server;
return 444;
}
# Option 2: Serve blank content (lowest resource usage)
server {
listen 80 default_server;
listen [::]:80 default_server;
access_log off;
return 200;
}
# Option 3: Custom error page (most user-friendly)
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/blank;
location / {
return 403;
}
}
The first option (return 444) is generally preferred because:
- Immediately closes the connection
- Consumes minimal server resources
- Prevents any content disclosure
- Works for both HTTP and HTTPS
Here's how to implement this alongside your regular vHosts:
# Default catch-all server
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444;
}
# Regular vHost example
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
For enhanced security, consider these additional measures:
# Block invalid Host headers
server {
listen 80 default_server;
server_name "";
return 444;
}
# HTTPS version (requires SSL certificate)
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /path/to/dummy.crt;
ssl_certificate_key /path/to/dummy.key;
return 444;
}
# Default catch-all server block
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 444; # Close connection immediately (most efficient method)
# Alternative options:
# return 403; # For explicit forbidden response
# rewrite ^(.*)$ /404.html; # For custom error page
}
Using status code 444 (Nginx-specific) is the most resource-efficient approach as it closes TCP connections without sending any response headers. This:
- Consumes minimal server resources
- Provides no information to potential attackers
- Won't appear in most access logs (depends on log_format)
server {
listen 80;
server_name example.com www.example.com;
# Your normal configuration here
root /var/www/example.com;
index index.html;
}
server {
listen 80;
server_name api.example.com;
# API-specific configuration
location / {
proxy_pass http://localhost:3000;
}
}
Test your configuration with:
# Check syntax
sudo nginx -t
# Verify default host behavior
curl -v http://your-server-ip
# Should show connection closed immediately
# Verify proper vhosts
curl -H "Host: example.com" http://your-server-ip
# Should return your site content
For production environments, consider adding:
# Block IP-based access to all vhosts
server {
listen 80 default_server;
server_name "";
return 444;
}
# Block invalid Host headers
if ($host !~* ^(example.com|www.example.com|api.example.com)$ ) {
return 444;
}
The 444 method outperforms alternatives because:
Method | CPU Impact | Network Impact |
---|---|---|
444 | Lowest | None (RST packet) |
403 | Medium | Full HTTP response |
Redirect | High | Full HTTP response |