Nginx Configuration: server_name “_” vs “” for Default Catch-All Server Blocks


1 views

When configuring Nginx as a reverse proxy or web server, you'll often need to set up a default catch-all server block to handle requests that don't match any defined server_name. Both server_name _ and server_name "" serve this purpose, but let's examine their technical differences.

The empty string ("") is actually the default value when no server_name is specified. Nginx uses this to match requests that don't have a Host header or don't match any configured server names.

server {
    listen 80;
    server_name ""; # matches empty Host header
    return 444; # Connection closed without response
}

The underscore (_) is simply a convention - a dummy name that will never match any real hostname. It's effectively identical to the empty string in functionality.

server {
    listen 80 default_server;
    server_name _;
    return 444;
}
  • Readability: _ is more explicit about being a catch-all
  • Documentation: Official Nginx docs often use _ as the example
  • Default Behavior: Not specifying any server_name is equivalent to ""

Both approaches have identical performance characteristics. Nginx processes them the same way internally during the server selection phase.

For maximum clarity and maintainability, I recommend using:

server {
    listen 80 default_server;
    server_name _;
    # Additional default server configuration
    return 444; # Or your preferred handling
}

This makes the catch-all intention immediately obvious to anyone reading the configuration.

Here's a more complex example showing how this fits into a production setup:

# Specific named servers
server {
    listen 80;
    server_name example.com www.example.com;
    # ... actual configuration
}

server {
    listen 80;
    server_name api.example.com;
    # ... API specific configuration
}

# Catch-all default server
server {
    listen 80 default_server;
    server_name _;
    location / {
        return 403 "Forbidden: Invalid host header";
    }
}

This structure ensures proper handling of both valid and invalid host headers.


When configuring Nginx as a web server, setting up a default catch-all server block is crucial for handling requests that don't match any defined virtual hosts. Both server_name _ and server_name "" approaches are commonly used, but what's the technical difference?

The Nginx documentation states that an empty server name ("") is automatically assigned if no server_name is specified. The underscore (_) is simply a convention that has no special meaning to Nginx - it's just an invalid domain name that will never match any real hostname.

# Both these configurations function identically:
server {
    listen 80 default_server;
    server_name "";  # Explicit empty name
    return 444;
}

server {
    listen 80 default_server;
    server_name _;   # Conventional placeholder
    return 444;
}

While both approaches work, there are subtle differences in behavior you should be aware of:

  • The empty string ("") is the official way to indicate a server block should match when no Host header is present
  • The underscore (_) is more readable and serves as clear documentation that this is a catch-all block
  • Performance-wise, there's no measurable difference between the two

For production environments, I recommend combining both the default_server flag and an explicit catch-all name:

server {
    listen 80 default_server;
    server_name _ "";
    return 444;  # Close connection silently
    # Alternatively: return 444 "No such host";
}

This configuration will handle:

  1. Requests with no Host header
  2. Requests with unknown hostnames
  3. Requests that would otherwise match no server block

You can verify your catch-all server block works using curl:

# Test with random hostname
curl -H "Host: random.example.com" http://your.server.ip

# Test with no host header
curl http://your.server.ip

Both commands should trigger your default server block response.

For more complex setups, you might want to:

server {
    listen 80 default_server;
    server_name _ "";
    
    location / {
        # Log unknown hosts for security monitoring
        access_log /var/log/nginx/unknown_hosts.log;
        
        # Redirect to a maintenance page
        rewrite ^ /maintenance.html break;
    }
}