Have you ever needed to calculate a pagination offset, build a composite cache key, or hash a request URI directly in your NGINX configuration? NGINX’s native configuration language cannot do arithmetic. You cannot add two numbers, multiply variables, or concatenate strings with computed values using only built-in directives. The set directive assigns static values, and the map directive handles conditional lookups, but neither can evaluate expressions like $page * 10 or $width * 2. The NGINX let module solves this problem by adding inline expression evaluation to your configuration files.
The module introduces the let directive, which evaluates mathematical expressions, performs string concatenation, executes bitwise operations, and computes cryptographic hashes — all inline within your configuration. No Lua scripting engine, no njs runtime, and no external dependencies beyond the module itself.
How the Module Works
The let directive extends the configuration parser with expression evaluation capabilities. When NGINX processes a request, the directive computes the result of an expression and stores it in a variable. This variable is then available to any other directive in the same request context, just like variables created by set or map.
Expressions are parsed at configuration time using a grammar-based parser (built with Bison), and the resulting expression tree is evaluated lazily at request time. This means the module adds zero overhead to requests that do not access the computed variable.
The NGINX let module supports:
- Arithmetic operations: addition (
+), subtraction (-), multiplication (*), division (/), and modulo (%) - String concatenation: the dot operator (
.) joins strings together - Bitwise operations: AND (
&) and OR (|) - Hexadecimal numbers: prefix with
0xfor hex literals - Parentheses: group sub-expressions to control evaluation order
- Built-in functions:
rand(),md5(),sha1(),sha256(),sha512(),length(),min(),max(),substr(), and more
Why Not Use Lua or njs Instead?
Both the Lua module and njs (NGINX JavaScript) can perform arithmetic and string operations. However, they embed entire scripting runtimes into NGINX. For simple calculations — a pagination offset, a bitwise mask, or a hash of a variable — the let directive is a lightweight alternative that keeps your configuration readable and avoids the complexity of embedding a programming language.
Use the NGINX let module when you need simple inline expressions. Reach for Lua or njs when you need loops, conditionals, HTTP subrequests, or complex logic.
Installation
RHEL, CentOS, AlmaLinux, Rocky Linux, and Amazon Linux
Install the module from the GetPageSpeed RPM repository:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-let
Then load the module by adding this line at the top of /etc/nginx/nginx.conf, before any http or events blocks:
load_module modules/ngx_http_let_module.so;
Verify the module loads correctly:
nginx -t
Debian and Ubuntu
First, set up the GetPageSpeed APT repository, then install:
sudo apt-get update
sudo apt-get install nginx-module-let
On Debian/Ubuntu, the package handles module loading automatically. No
load_moduledirective is needed.
For package details, see the APT module page.
The let Directive
The module provides a single directive:
Syntax
let $variable <expression>;
Contexts
http, server, location
Description
Evaluates <expression> and assigns the result to $variable. The expression is parsed at configuration load time, but evaluated lazily when the variable is accessed during request processing.
Critical Syntax Rule: Spaces Between Tokens
The let directive uses the NGINX configuration parser as its lexer. Therefore, you must add spaces around every operator, parenthesis, and function argument. This is the most common source of configuration errors with this module.
# WRONG - will cause a parse error
let $value (1+2);
let $value 1 + (2 * $uid);
# CORRECT - spaces around every token
let $value ( 1 + 2 );
let $value 1 + ( 2 * $uid );
Arithmetic Operations
The let directive supports five arithmetic operators that work with integer values.
Addition and Subtraction
set $base_price 100;
set $tax 15;
let $total $base_price + $tax;
# $total = 115
let $discount $total - 20;
# $discount = 95
Multiplication and Division
set $page_num 5;
set $per_page 10;
let $offset $page_num * $per_page;
# $offset = 50
set $total_items 100;
let $half $total_items / 2;
# $half = 50
Modulo
The modulo operator returns the remainder after division. It is particularly useful for distributing requests across buckets:
set $user_id 17;
let $bucket $user_id % 3;
# $bucket = 2
Operator Precedence and Grouping
The module respects standard mathematical operator precedence. Multiplication, division, and modulo bind more tightly than addition and subtraction. Use parentheses to override the default order:
# Without parentheses: multiplication first
let $result1 10 + 20 * 3;
# $result1 = 70 (20*3=60, then 60+10=70)
# With parentheses: addition first
let $result2 ( 10 + 20 ) * 3;
# $result2 = 90
You can nest parentheses for complex expressions:
set $a 10;
set $b 3;
let $result ( $a + $b ) * ( $a - $b );
# $result = 91 (13 * 7)
Hexadecimal Numbers
The module accepts hexadecimal literals with the 0x prefix:
let $max_color 0xFF + 1;
# $max_color = 256
String Concatenation
The dot operator (.) concatenates strings, similar to PHP or Perl:
set $first_name "John";
set $last_name "Doe";
let $full_name $first_name . " " . $last_name;
# $full_name = "John Doe"
You can combine NGINX built-in variables with string concatenation to build dynamic values:
let $cache_key $scheme . "://" . $host . $request_uri;
# For a request to http://example.com/page?id=5:
# $cache_key = "http://example.com/page?id=5"
Bitwise Operations
The module supports bitwise AND (&) and OR (|) operators. These are useful for flag manipulation and IP address calculations:
# Extract the lower nibble
set $flags 0xAB;
let $lower_nibble $flags & 0x0F;
# $lower_nibble = 11 (0x0B in decimal)
# Combine flags
let $combined 0x0F | 0xF0;
# $combined = 255 (0xFF)
Built-in Functions
The module includes several built-in functions that extend its capabilities beyond simple arithmetic.
rand() — Random Number Generation
Generates a random integer:
let $random_value rand();
# $random_value = a random integer (e.g., 1286293501)
Cryptographic Hash Functions
The module provides a suite of hash functions. Each takes a single string argument and returns the hexadecimal digest:
| Function | Output Length | Algorithm |
|---|---|---|
md5() |
32 chars | MD5 |
sha1() |
40 chars | SHA-1 |
sha224() |
56 chars | SHA-224 |
sha256() |
64 chars | SHA-256 |
sha384() |
96 chars | SHA-384 |
sha512() |
128 chars | SHA-512 |
ripemd160() |
40 chars | RIPEMD-160 |
md4() |
32 chars | MD4 |
Example usage:
let $uri_hash md5( $request_uri );
# Produces a 32-character hex MD5 digest of the request URI
let $secure_hash sha256( $request_uri );
# Produces a 64-character hex SHA-256 digest
Note the required syntax: the function name is immediately followed by ( with no space, and the closing ) is separated by a space from the argument:
# CORRECT function call syntax
let $hash md5( $uri );
let $val rand();
# WRONG - space before opening parenthesis
let $hash md5 ( $uri );
For simpler hashing needs without installing an additional module, consider the NGINX set-misc module which also provides set_md5 and set_sha1 directives.
length() — String Length
Returns the length of a string in bytes:
let $uri_length length( $request_uri );
# For /page?id=5: $uri_length = 10
min() and max() — Integer Comparison
Return the minimum or maximum of two integer values:
set $a 42;
set $b 17;
let $smaller min( $a $b );
# $smaller = 17
let $larger max( $a $b );
# $larger = 42
Note that function arguments are separated by spaces, not commas.
substr() — Substring Extraction
Extracts a substring from a string, given an offset and length:
set $full_string "Hello_World_Test";
let $extracted substr( $full_string 6 5 );
# $extracted = "World"
The syntax is substr( string offset length ) where offset is zero-based and length specifies how many characters to extract.
Practical Examples
Pagination Offset Calculation
Calculate database query offsets from page numbers:
location ~ ^/api/items/page/(?P<page_num>\d+)$ {
set $per_page 25;
let $db_offset $page_num * $per_page;
proxy_pass http://backend/api/items?offset=$db_offset&limit=$per_page;
}
Dynamic Cache Key Construction
Build composite cache keys from multiple request attributes:
let $cache_key $scheme . "://" . $host . $request_uri;
proxy_cache_key $cache_key;
Worker Count Calculation
Compute derived configuration values:
set $cpu_cores 8;
let $worker_connections $cpu_cores * 2;
Request Distribution with Modulo
Distribute requests across backend groups using the URI length as a simple hash:
let $uri_len length( $request_uri );
let $backend_bucket $uri_len % 3;
Important Caveats
NGINX Variables Are Global
NGINX variables are defined globally at configuration time, even though they are evaluated per-request. If you use the same variable name in multiple let directives across different locations, the last definition wins for all locations:
# WARNING: This will NOT work as expected
location /page-a {
let $result 10 + 20;
# $result will NOT be 30
}
location /page-b {
let $result 50 + 50;
# $result = 100 in BOTH locations
}
Always use unique variable names for each let expression:
location /page-a {
let $result_a 10 + 20;
# $result_a = 30
}
location /page-b {
let $result_b 50 + 50;
# $result_b = 100
}
This is the same behavior as the native map directive and is a fundamental aspect of NGINX variable architecture, not a limitation of this module specifically.
Integer Arithmetic Only
The module operates on signed integers. There is no floating-point support. Division truncates toward zero:
let $result 7 / 2;
# $result = 3, not 3.5
No Division by Zero Protection
Dividing by zero will cause a runtime error. Ensure your divisor variables are never zero before using them in division or modulo operations.
How Let Compares to Native NGINX Alternatives
NGINX provides the set and map directives natively, and they cover many variable assignment scenarios. Here is how they compare to the let directive:
| Capability | set |
map |
let |
|---|---|---|---|
| Assign static value | Yes | Yes | Yes |
| Copy variable | Yes | Yes | Yes |
| Conditional mapping | No | Yes | No |
| Arithmetic | No | No | Yes |
| String concatenation | No | No | Yes |
| Bitwise operations | No | No | Yes |
| Hash functions | No | No | Yes |
| Regex matching | No | Yes | No |
Use set for simple assignments, map for conditional lookups, and let for computed values. For a deep dive into the map directive, see NGINX map Directive: Guide to Conditional Variables.
Troubleshooting
“unknown directive let”
The module is not loaded. Add load_module modules/ngx_http_let_module.so; at the top of nginx.conf, before any http or events blocks.
Parse Errors in let Expressions
Missing spaces between tokens. Remember that every operator, parenthesis, and function argument must be separated by spaces:
# Common mistakes
let $val (1+2); # Missing spaces
let $val $a+$b; # Missing spaces around +
let $val md5($uri); # Space needed before )
let $val md5 ( $uri ); # No space before (
# Correct versions
let $val ( 1 + 2 );
let $val $a + $b;
let $val md5( $uri );
“let variable X not found”
The variable referenced in the expression does not exist or has not been initialized. Verify that any variables used in let expressions are defined (via set, map, or NGINX built-in variables) before the let directive accesses them.
“let undefined function”
You called a function that does not exist. The available functions are: rand, md4, md5, sha1, sha224, sha256, sha384, sha512, ripemd160, length, substr, min, and max.
Same Variable Produces Wrong Values
You are reusing the same variable name across multiple let directives. NGINX variables are global — use unique names for each expression. See the “NGINX Variables Are Global” section above.
Performance Considerations
The NGINX let module has minimal performance impact:
- Configuration time: Expressions are parsed once during
nginx -tor startup. The Bison-generated parser builds an expression tree that persists for the lifetime of the worker process. - Request time: Expression evaluation traverses the pre-built tree. Simple arithmetic operations complete in nanoseconds. Hash functions (SHA-256, SHA-512) add microseconds per computation, proportional to the input size.
- Memory: Each
letdirective allocates a small expression tree from the configuration pool. This memory is shared across all worker processes and is never freed until NGINX shuts down.
For high-throughput scenarios where you compute hashes on every request, benchmark your specific workload. The hash functions use OpenSSL internally and benefit from hardware acceleration on modern CPUs.
Conclusion
The NGINX let module fills a gap in NGINX’s configuration language by adding inline expression evaluation. It handles arithmetic, string concatenation, bitwise operations, and cryptographic hashing without requiring a full scripting runtime. For system administrators who need computed variables in their configuration, this is a focused, lightweight solution.
The module is available as a pre-built package for RHEL-based distributions via the GetPageSpeed RPM repository and for Debian/Ubuntu via the APT repository. The source code is on GitHub.
For more NGINX modules that extend variable handling, explore the NGINX echo module for debugging, the NGINX array variables module for array operations, and the NGINX JSON var module for building JSON structures from NGINX variables.

