How to Use Variables in Nginx server_name Directive: A Practical Guide for Developers


2 views

Many developers try to use variables in Nginx's server_name directive, only to find it doesn't work as expected. The configuration below is a common attempt:

server {
    listen 80;
    index index.php index.html;
    set $foo "bar.example.com";
    server_name $foo;
    # This won't work!
}

Nginx needs to know all possible server names during configuration parsing phase, before requests are processed. Since variables are evaluated at request time, they can't be used in server_name.

Here are practical workarounds depending on your use case:

1. Using Multiple server Blocks

The most straightforward solution is to list all possible server names:

server {
    listen 80;
    server_name bar.example.com baz.example.com;
    # ... other config
}

2. Using Regular Expressions

For dynamic subdomains, use regex patterns:

server {
    listen 80;
    server_name ~^(?.+)\.example\.com$;
    
    location / {
        # Use captured subdomain
        set $site_root /sites/$subdomain;
        root $site_root;
    }
}

3. Using Map Directive

For more complex mappings between domains and variables:

map $http_host $site_root {
    default         /sites/default;
    bar.example.com /sites/bar;
    baz.example.com /sites/baz;
}

server {
    listen 80;
    server_name bar.example.com baz.example.com;
    
    root $site_root;
}

For truly dynamic domains (like SaaS applications), consider:

  • Generating Nginx config files programmatically
  • Using OpenResty with Lua scripts
  • Implementing a custom module

Remember that regex server names and maps have performance implications:

  • Place exact matches first in server_name lists
  • Order regex patterns from most specific to most general
  • Use server_names_hash_max_size and server_names_hash_bucket_size for large numbers of domains

Many Nginx users encounter a frustrating limitation when trying to use variables in the server_name directive. The configuration you attempted:

server {
    listen 80;
    index index.php index.html;
    set $foo "bar.example.com";
    server_name $foo;
}

Doesn't work because Nginx evaluates the server_name directive during configuration parsing, before variables are available at runtime.

Nginx needs to determine which server block to use for incoming requests very early in the processing pipeline. Server name matching happens:

  • Before request processing begins
  • During the configuration loading phase
  • When the server name map is built

Here are several approaches to achieve dynamic server name handling:

1. Using Regular Expressions

server {
    listen 80;
    server_name ~^(www\.)?(?.+)$;
    
    # Use captured group
    set $full_domain $domain;
    if ($http_host ~* ^www\.(.*)$) {
        set $full_domain $1;
    }
    
    # Now use $full_domain in other directives
    root /var/www/$full_domain;
}

2. Multiple Server Blocks with Includes

# main.conf
server {
    listen 80;
    include /etc/nginx/conf.d/sites/*.conf;
}

# /etc/nginx/conf.d/sites/bar.example.com.conf
server_name bar.example.com;
root /var/www/bar;
# other directives...

3. Using map Directive for Dynamic Routing

map $http_host $backend {
    default default_backend;
    "~*.example.com" $http_host;
}

server {
    listen 80;
    server_name .example.com;
    
    location / {
        proxy_pass http://$backend;
    }
}

For cases requiring true dynamic hostnames, consider:

server {
    listen 80 default_server;
    
    # Extract host without port
    set $host_without_port $host;
    if ($host ~* ^([^:]+)) {
        set $host_without_port $1;
    }
    
    # Use in rewrites or other directives
    if ($host_without_port ~* ^(.+)\.example\.com$) {
        set $customer $1;
        rewrite ^ /$customer$uri;
    }
    
    location / {
        # Process based on $customer variable
    }
}

Remember that regular expressions in server_name are evaluated in order:

  • Exact names first (most efficient)
  • Wildcard names starting with *
  • Wildcard names ending with *
  • Regular expressions (least efficient)

For large configurations, prefer exact matches or wildcards over regex when possible.