Nginx Server Block Selection: default_server, server_name, and location Matching

1 min readWeb Development

Nginx selects a server block by matching the Host header against server_name directives. If no match, it uses the default_server (or the first block if none is declared). The _ server_name is a conventional catch-all — not a wildcard. location blocks have a precedence order: = (exact) > ^~ (prefix priority) > ~ (regex) > plain prefix.

nginxconfiguration

default_server: fallback for unmatched hosts

# Without default_server: nginx uses the first block in config order as fallback
server {
    listen 80;
    server_name api.example.com;
    # ...
}

# With default_server: explicit fallback (only one allowed per IP:port)
server {
    listen 80 default_server;
    server_name _;
    return 444;  # close connection for unknown hosts
}

listen 80 without default_server: nginx uses whichever server block appears first in config files for requests that don't match any server_name.

listen 80 default_server: explicitly designates this block as the fallback. Useful for rejecting requests to unknown hosts (return 444 closes the connection with no response) or serving a landing page.

One default_server per IP:port combination. Multiple default_server directives on the same port cause nginx -t to fail with:

nginx: [warn] conflicting server name "_" on 0.0.0.0:80, ignored

server_name _: a catch-all convention

server_name _ is not a wildcard syntax. _ is simply a hostname that no real client will ever send — it's a convention for "this block matches nothing by name, use it as a catch-all when combined with default_server":

server {
    listen 80;
    server_name example.com;
    # matches: Host: example.com
}

server {
    listen 80;
    server_name _;  # matches requests with Host: _  (nobody sends this)
    # This block is NOT automatically chosen for unmatched hosts
    # Without default_server, the FIRST block is the fallback
}

To test which block handles a request:

# Force the Host header to see which block matches
curl localhost:80/health -H "Host: _"
curl localhost:80/health -H "Host: doesnotexist.com"

location block matching order

nginx evaluates location blocks in this precedence order:

1. = (exact match)     → highest priority
2. ^~ (prefix, then stop searching for regex)
3. ~ / ~* (regex, case-sensitive / case-insensitive) → in order of appearance
4. Plain prefix strings → longest match wins
server {
    # 1. Exact match — checked first
    location = /health {
        return 200 'OK';
    }

    # 2. Prefix with regex suppression
    location ^~ /static/ {
        root /var/www;
    }

    # 3. Case-sensitive regex
    location ~ \.php$ {
        fastcgi_pass php-fpm:9000;
    }

    # 3. Case-insensitive regex
    location ~* \.(jpg|png|gif)$ {
        expires 30d;
    }

    # 4. Plain prefix (longest match wins)
    location / {
        proxy_pass http://backend;
    }
}

location ~ \.php$ — the ~ triggers case-sensitive regex matching. Without ~, nginx treats the pattern as a literal string, so /index.php wouldn't match .php$.

Nginx does not have a built-in wildcard for server_name — use wildcard syntax *.example.com for subdomains

GotchaNginx

server_name _ is a convention, not a wildcard. Real nginx wildcard syntax is *.example.com (matches any single subdomain) or .example.com (matches the domain and all subdomains). Regex server_name uses tilde prefix: ~^api\d+\.example\.com$. The _ catch-all works only because real clients don't send 'Host: _'. If a client sends any legitimate Host that doesn't match other blocks, and the _ block isn't marked default_server, nginx falls back to the first block — not the _ block.

Prerequisites

  • HTTP Host header
  • Nginx server block matching
  • Virtual hosting

Key Points

  • server_name _; — catch-all convention (no real client sends Host: _). Not a wildcard.
  • server_name *.example.com; — matches api.example.com, www.example.com, etc.
  • default_server — explicit fallback for unmatched hosts. Without it, first server block is the fallback.
  • nginx -t — validate config; catches duplicate default_server, syntax errors, file not found.

Forwarding protocol through a load balancer

AWS ELB/ALB terminates HTTPS and forwards HTTP to nginx. The application behind nginx sees HTTP and may generate incorrect HTTP redirect URLs. Forward the original protocol:

server {
    listen 80;
    server_name api.example.com;

    location ~ \.php$ {
        fastcgi_pass php-fpm:9000;
        # Forward original protocol from ELB to application
        fastcgi_param HTTP_X_FORWARDED_PROTO $http_x_forwarded_proto;
        include fastcgi_params;
    }

    location / {
        proxy_pass http://app-backend;
        # For proxy_pass, use proxy_set_header instead
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Host $host;
    }
}

$http_x_forwarded_proto reads the X-Forwarded-Proto header that the ALB sets (https). The application reads $_SERVER['HTTP_X_FORWARDED_PROTO'] (PHP) or the equivalent to know the original scheme.

You have three nginx server blocks all listening on port 80, none with default_server. A request arrives with Host: unknown.example.com that matches none. Which server block handles it?

easy

No default_server is declared. Nginx must fall back to something when no server_name matches.

  • ANginx returns 404 for unmatched hosts
    Incorrect.Nginx doesn't return 404 — it selects a server block to handle the request. 404 is returned by the selected block if the path doesn't exist, not by the server selection mechanism itself.
  • BThe first server block in the configuration file order handles the request
    Correct!When no server_name matches and no default_server is declared, nginx uses the first server block that listens on that IP:port combination. The 'first' is determined by the order config files are loaded (usually alphabetical in /etc/nginx/sites-enabled/ or /conf.d/). This is why explicitly declaring a default_server catch-all block is good practice — it makes the fallback behavior intentional and predictable, rather than accidentally serving a different site's content for unknown hostnames.
  • CThe last server block handles the request
    Incorrect.The fallback without default_server is the first block, not the last.
  • DNginx closes the connection with no response
    Incorrect.Closing connections without response requires `return 444` in a server block. By default, nginx selects a block and processes the request.

Hint:What does nginx use as a fallback when no server_name matches and no default_server is set?