Many developers coming from programming backgrounds expect Nginx variables to work like typical programming variables, but there are important limitations:
# This works for simple string substitution
set $port 80;
# This WON'T work for complex directives
# set $listen "listen $port;"; # Syntax error
For your specific use case, here's a working approach:
server {
# Define core variables
set $root_path /web/sites;
set $site_type live;
# Domain-specific configuration
server_name domain.com *.domain.com;
# Dynamic root path construction
root $root_path/domain.com/$site_type/public_html/current/;
# Other directives using variables
access_log $root_path/domain.com/$site_type/logs/access.log;
error_log $root_path/domain.com/$site_type/logs/error.log;
# PHP handling
location ~ \.php$ {
fastcgi_param SITE_TYPE $site_type;
# ... other FastCGI params
}
}
For multiple environments, consider this pattern:
# /etc/nginx/site-templates/base.conf
root /web/sites/$domain/$site_type/public_html/current/;
access_log /web/sites/$domain/$site_type/logs/access.log;
# /etc/nginx/conf.d/site1.conf
server {
set $domain "example.com";
set $site_type "production";
include /etc/nginx/site-templates/base.conf;
# site-specific overrides
listen 443 ssl;
}
Remember that in Nginx:
- Variables are scoped to the configuration level where they're defined
- You can't use variables in all directives (like
listen
) - Map blocks often work better than conditionals for complex logic
For truly dynamic configurations, consider:
# Using map for conditional logic
map $host $subdomain {
"~^(?.+)\.domain\.com$" $sub;
default "";
}
server {
# Now $subdomain contains the subdomain or empty string
root /web/sites/domain.com/$subdomain/public_html/;
}
Remember that Nginx configuration is primarily declarative, not imperative. For complex dynamic needs, you might want to generate configurations programmatically before Nginx loads them.
Many developers attempt to use Nginx variables in ways similar to programming languages, but there are important limitations to understand. The set
directive does work in Nginx configuration, but its usage scope is more restricted than you might expect.
# This works for simple variable assignment
set $port 80;
set $domain "example.com";
# But these variables have limited usage contexts
# They CANNOT be used in all configuration directives
The key issue in your configuration attempt is trying to use variables in directives where Nginx doesn't support them. Here's what works and what doesn't:
# Supported uses:
set $my_var "value";
if ($http_user_agent ~* "mobile") {
set $mobile true;
}
# Unsupported uses:
# server_name $domain; # Variables not allowed here
# root /path/$domain; # Variables not allowed in root path
For dynamic site configurations, consider these approaches:
# Approach 1: Use include files with different configs
server {
include /etc/nginx/sites-available/example.com.conf;
}
# Approach 2: Use map directive for conditional logic
map $http_host $root_path {
default "/web/sites/default";
"~^(?[^.]+)\.example\.com" "/web/sites/example.com/$subdomain";
}
Here's how I handle different environments (dev/stage/prod) in my Nginx configs:
# Define environment variable (set in OS or main nginx.conf)
env DEPLOY_ENV;
# Main server block
server {
listen 80;
# Use lua for more complex logic if needed
set_by_lua $root_path '
local env = os.getenv("DEPLOY_ENV") or "dev"
return "/web/sites/" .. ngx.var.host .. "/" .. env
';
root $root_path;
}
For logging, you can use variables in some contexts:
# This works for access_log
access_log /var/log/nginx/${host}-access.log;
# But error_log path cannot contain variables
error_log /var/log/nginx/error.log;
- Use separate config files for different sites/environments
- Leverage Nginx's include directive for modularity
- Consider using configuration management tools (Ansible, Chef) for generation
- For complex logic, explore Nginx+Lua (OpenResty)