yum upgrades for production use, this is the repository for you.
Active subscription is required.
Technical Briefing: NGINX Variables — Semantics, Caching, and Scope
The Problem
NGINX variables look like scripting variables but do not behave like them. Three rules govern their behavior, and violating any of them produces either a startup failure or a silent, hard-to-diagnose bug:
- A variable must be introduced by a directive (
set,map,geo) or exported by a module. Referencing anything else causes NGINX to refuse to start withnginx: [emerg] unknown "foo" variable. - The name is global to the config, but storage is allocated per request.
- Each value is computed by a handler that may or may not cache.
Two Kinds of Built-In Variables
Variables mapped to a concrete piece of the request:
– $uri — the normalized, URL-decoded path with the query string stripped.
– $request_uri — the raw request target exactly as the client sent it, query string and all.
Infinite families computed on the spot by a get handler:
– $arg_XXX, $http_XXX, $cookie_XXX.
Reading $arg_name re-scans the query string every time. On hot paths, compute it once with set and reuse the result.
Writable Built-Ins
Most built-ins are read-only, but $args is deliberately writable. Rewriting $args changes the query string NGINX forwards upstream and changes what subsequent $arg_XXX reads return — useful for normalizing or injecting query parameters before a proxy_pass.
map Is Lazy and Cached
The lookup only happens when the result variable is first read, so defining dozens of map variables costs nothing for requests that don’t use them. Once read, however, the result is stored: if the source changes later in the same request, $rate keeps returning its first answer. This caching explains a whole class of “why didn’t my variable update” bugs.
Three Internal States: Real Value, Empty String, and “Not Found”
In plain config all three render as an empty string, so if ($arg_filter = "") cannot distinguish “present but empty” from “absent.” The Lua module can: a missing argument reads as nil, a present-but-empty one reads as "". That distinction matters for APIs where ?filter= (clear the filter) differs from omitting filter entirely.
Value Manipulation and List Handling Come from Modules
set-miscprovidesset_unescape_uri,set_escape_uri,set_by_lua,set_quote_sql_str,set_encode_base64, hashing, and more.array-varsplits a delimited string into an array, transforms elements viaarray_mapwith the$array_ititerator placeholder, and joins back witharray_join— all in config, no scripting language.
Request-Flow Propagation Differs
- Internal redirects (
rewrite ... last,try_files,error_page,echo_exec) stay the same request, so the variable container carries over and your own variables persist while$uriupdates. - Subrequests (
echo_location,auth_request, SSIinclude) get their own container, so values set inside do not leak back to the parent. - Exception:
auth_requestdeliberately shares variables in one direction so an auth subrequest can hand values back to the main request.
Common Pitfalls
- Referencing an undeclared variable.
- Assuming
setordering follows textual position — it runs in the rewrite phase, well before content. - Expecting
$arg_xand amapvariable to behave the same. - Confusing empty with absent.
- Re-reading the
$arg_,$http_,$cookie_families on hot paths instead of computing once.
Setup for the Runnable Examples
Four modules are needed: echo, set-misc, lua, array-var.
RHEL-based systems:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-echo nginx-module-set-misc nginx-module-lua nginx-module-array-var
Load each module near the top of nginx.conf, with ndk_http_module.so first since set-misc, lua, and array-var build on the NGINX Development Kit (the nginx-module-ndk package is pulled in automatically as a dependency):
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_echo_module.so;
load_module modules/ngx_http_set_misc_module.so;
load_module modules/ngx_http_lua_module.so;
load_module modules/ngx_http_array_var_module.so;
Debian/Ubuntu: set up the APT repository, then:
sudo apt-get update
sudo apt-get install nginx-module-echo nginx-module-set-misc nginx-module-lua nginx-module-array-var
The package handles module loading automatically, so no load_module directive is needed.
Operational Note
Keeping configs correct as they evolve is a separate problem — upgrades, module updates, and everyday edits quietly reintroduce misconfigurations. Scheduled gixy scans tied to live NGINX runtime metrics address this, and the agent is drop-in compatible with the deprecated nginx-amplify-agent (EOL January 2026).
Read the full article: NGINX Variables: Scope, Handlers, and Common Pitfalls
