Optimal Nginx Configuration: How to Disable Default vHost and Allow Only Specified Domains


1 views

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