What Nginx Variables Actually Are
Nginx variables, written with a leading dollar sign like $host or $remote_addr, are not general-purpose programming variables — they are lazily evaluated per-request values computed by Nginx core or loaded modules. A variable like $request_time doesn't hold a static value set once; it's recomputed fresh for every single request that references it, and referencing a variable that no module defines (a typo, essentially) causes Nginx to fail at configuration load time with an 'unknown variable' error rather than silently defaulting to an empty string.
Cricket analogy: It's like a scoreboard's 'current run rate' figure: it isn't a number typed in once, it's recalculated fresh every ball based on the live match state, and referencing a stat the scoring system doesn't track at all just breaks the display.
Commonly Used Built-in Variables
$host holds the request's Host header (falling back to the server_name if absent), $remote_addr is the client's IP address, $request_uri is the full original URI including query string, $args is just the query string, $uri is the normalized current URI (which changes after an internal rewrite), and $document_root reflects whatever root directive is active for the current location. Because $uri updates after a rewrite while $request_uri never does, mixing them up in logging or proxy headers is a frequent source of confusing debug data.
Cricket analogy: It's like the difference between a player's 'current form rating' that updates after every innings ($uri, updates after processing) versus their 'debut match record' that's permanently fixed and never changes ($request_uri, frozen at the original request).
Using map to Derive Custom Variables
The map directive, used in the http context, creates a new variable whose value is computed by matching another variable against a lookup table, for example mapping $http_user_agent to a boolean $is_bot flag, or mapping $http_origin to an allowed CORS header value. This is far more efficient and readable than chains of nested if statements, which Nginx documentation explicitly warns against using inside location blocks because if interacts unpredictably with other directives in that context.
Cricket analogy: It's like a lookup table mapping bowler names to their preferred field settings — instead of a coach shouting nested conditional instructions mid-over, the table is pre-defined and instantly applied.
http {
map $http_user_agent $is_bot {
default 0;
"~*(bot|crawl|spider)" 1;
}
map $http_origin $cors_origin {
default "";
"https://app.example.com" $http_origin;
"https://admin.example.com" $http_origin;
}
server {
listen 80;
server_name example.com;
location / {
if ($is_bot) {
return 403;
}
add_header Access-Control-Allow-Origin $cors_origin always;
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Original-URI $request_uri;
}
}
}Use map in the http context to precompute derived variables once per request efficiently, instead of scattering nested if blocks across location contexts — the official Nginx documentation calls if inside location blocks 'evil' precisely because of how unpredictably it interacts with other directives.
Referencing an undefined variable, such as a typo like $remote_addrr, is a hard configuration error — Nginx refuses to start rather than silently treating it as empty. Always run nginx -t immediately after introducing a new variable reference to catch typos before reloading a production instance.
- Nginx variables are lazily evaluated per-request, not static values assigned once at config load time.
- $host is the effective hostname, $remote_addr is the client IP, $request_uri is the full original URI plus query string.
- $uri reflects the current, possibly-rewritten URI, while $request_uri always stays frozen as the original.
- $args holds just the query string; $document_root reflects the active root directive for the current location.
- map computes a new variable from a lookup table in the http context, far cleaner than nested if statements.
- Nginx's own docs warn that if inside a location block behaves unpredictably and should be avoided when possible.
- Referencing an undefined variable is a hard startup error, not a silent empty-string fallback.
Practice what you learned
1. What is the key behavioral difference between $uri and $request_uri?
2. What happens when Nginx configuration references a variable that no core module or loaded module defines?
3. What is the recommended way to derive a boolean-like variable (e.g. is this request from a bot) from another variable?
4. Which variable holds just the query string portion of a request, without the path?
5. Why does Nginx documentation describe using if inside a location block as risky?
Was this page helpful?
You May Also Like
Locations and Matching Rules
How Nginx's location block matching algorithm actually works — prefix, exact, and regex modifiers, and the precedence order that trips up most misconfigurations.
Rewrites and Redirects
The difference between client-visible redirects (return) and internal URI rewrites (rewrite) in Nginx, plus the flags and pitfalls that cause loops and lost query strings.
Logging in Nginx
How access logs and error logs work in Nginx, the difference between request_time and upstream_response_time for diagnosing latency, and how to control log volume in production.
Related Reading
Related Study Notes in DevOps
Browse all study notesAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics