yum upgrades for production use, this is the repository for you.
Active subscription is required.
Modern web architectures rely heavily on JSON. API gateways receive JSON payloads, microservices exchange JSON messages, and monitoring systems expect structured JSON logs. However, NGINX — despite being the most popular reverse proxy for these architectures — has no built-in ability to parse JSON data. The NGINX JSON module fills that gap.
The NGINX JSON module (ngx_http_json_module) adds two simple directives — json_loads and json_dumps — that let you parse JSON strings and extract individual fields directly in your NGINX configuration. No Lua scripting, no njs runtime, no external dependencies at request time. Just clean, directive-based JSON parsing.
In this article, you will learn how to install the NGINX JSON module, configure it for common use cases, and leverage it for tasks like structured logging, header-based routing, and multi-tenant proxying.
How the NGINX JSON Module Works
The NGINX JSON module uses the jansson C library for JSON parsing. It integrates with the NGINX Development Kit (NDK) to expose two set-variable directives:
json_loads— Parses a string containing JSON into an internal JSON variablejson_dumps— Extracts a value from the parsed JSON variable into a string variable, optionally following a path of keys
The parsing happens at request time using NGINX’s variable evaluation mechanism. When a variable created by json_dumps is accessed (for example, in a return directive, proxy_set_header, or log_format), NGINX evaluates the JSON path and returns the extracted value.
This approach is more efficient than a full scripting runtime because it only parses what is actually needed. If a JSON variable is never accessed during request processing, the parsing never occurs.
Installation
The NGINX JSON module is available as a prebuilt dynamic module from the GetPageSpeed repository. Installing it takes just two commands.
RHEL, CentOS, AlmaLinux, Rocky Linux, and Amazon Linux
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-json
This automatically installs the required nginx-module-ndk dependency.
After installation, enable both modules by adding these lines at the very top of /etc/nginx/nginx.conf, before any other directives:
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_json_module.so;
Important: The NDK module must be loaded before the JSON module. NGINX loads modules in the order they appear, and ngx_http_json_module depends on symbols provided by ndk_http_module.
Debian and Ubuntu
First, set up the GetPageSpeed APT repository, then install:
sudo apt-get update
sudo apt-get install nginx-module-json
On Debian and Ubuntu, the package handles module loading automatically. No load_module directive is needed.
Verify the Installation
After adding the load_module directives (RHEL-based systems), verify that NGINX accepts the configuration:
sudo nginx -t
You should see:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
If you see unknown directive "json_loads" errors later when adding configuration, it means the module is not loaded. Double-check the load_module lines at the top of your nginx.conf.
Directive Reference
The NGINX JSON module provides exactly two directives. Both are available in the http, server, and location contexts.
json_loads
Syntax: json_loads $json_variable input_string;
Context: http, server, location
Parses input_string as JSON and stores the result in $json_variable. The input string can contain NGINX variables (for example, $http_x_data or $arg_payload).
If the input is not valid JSON, NGINX logs an error and returns a 500 status code.
json_dumps
Syntax: json_dumps $string_variable $json_variable [key ...];
Context: http, server, location
Extracts a value from $json_variable and stores it in $string_variable.
- Without path keys: Serializes the entire JSON object as a compact string
- With path keys: Navigates the JSON structure using the provided keys. For objects, keys are field names. For arrays, keys are numeric indices (starting from 0)
When the path points to a string value, the raw string is returned (without quotes). For non-string values (numbers, booleans, null, nested objects, and arrays), the JSON representation is returned.
Practical Examples
All examples below have been tested on Rocky Linux 10 with NGINX 1.28 and nginx-module-json version 0.
Extract Fields from a JSON String
The most basic use case is parsing a JSON string and extracting individual fields:
location /api/status {
set $json_string '{"service":"payment-api","version":"2.1.0","healthy":true}';
json_loads $json $json_string;
json_dumps $service $json service;
json_dumps $version $json version;
json_dumps $healthy $json healthy;
return 200 "service=$service version=$version healthy=$healthy\n";
}
A request to this endpoint returns:
service=payment-api version=2.1.0 healthy=true
Notice that string values like payment-api are returned without quotes, while non-string values like true are returned as their JSON representation.
Navigate Nested JSON Objects
The json_dumps directive accepts multiple keys to traverse nested objects:
location /api/user {
set $data '{"user":{"name":"Alice","address":{"city":"London","zip":"SW1A"}}}';
json_loads $json $data;
json_dumps $name $json user name;
json_dumps $city $json user address city;
json_dumps $zip $json user address zip;
return 200 "name=$name city=$city zip=$zip\n";
}
Result:
name=Alice city=London zip=SW1A
Each key navigates one level deeper into the JSON structure. The path user address city is equivalent to the JavaScript expression json.user.address.city.
Access JSON Array Elements
Use numeric indices to access array elements:
location /api/items {
set $data '{"items":["apple","banana","cherry"]}';
json_loads $json $data;
json_dumps $first $json items 0;
json_dumps $second $json items 1;
json_dumps $third $json items 2;
return 200 "first=$first second=$second third=$third\n";
}
Result:
first=apple second=banana third=cherry
Array indices are zero-based, just like in most programming languages.
Dump the Full JSON Object
Call json_dumps without any path keys to serialize the entire JSON object as a compact string:
location /api/echo {
set $data '{"status":"ok","count":42}';
json_loads $json $data;
json_dumps $output $json;
return 200 "$output\n";
}
Result:
{"status":"ok","count":42}
This is useful for logging or forwarding the full JSON payload.
Parse JSON from Request Headers
A powerful use case is extracting fields from JSON passed in custom HTTP headers. Unlike query string arguments, header values are not URL-encoded, so they work directly with json_loads:
location /api/process {
json_loads $json $http_x_request_context;
json_dumps $tenant $json tenant;
json_dumps $role $json role;
proxy_set_header X-Tenant $tenant;
proxy_set_header X-Role $role;
proxy_pass http://backend;
}
A client sends:
curl -H 'X-Request-Context: {"tenant":"acme","role":"admin"}' \
https://example.com/api/process
NGINX parses the JSON header, extracts the tenant and role fields, and forwards them as separate headers to the backend. This eliminates the need for the backend to parse the JSON itself. For more advanced header manipulation, see the NGINX Headers-More module.
Structured Logging with JSON Fields
Combine the NGINX JSON module with NGINX’s log_format to embed extracted JSON fields in your access logs:
log_format enriched escape=json
'{"time":"$time_iso8601",'
'"status":$status,'
'"uri":"$uri",'
'"service":"$service"}';
server {
listen 80;
location /api/ {
set $metadata '{"service":"user-api","version":"3.0"}';
json_loads $json $metadata;
json_dumps $service $json service;
access_log /var/log/nginx/api_access.log enriched;
proxy_pass http://backend;
}
}
Each log entry now includes the service field extracted from the JSON metadata:
{"time":"2026-03-09T12:32:43+00:00","status":200,"uri":"/api/users","service":"user-api"}
This makes logs easier to filter and aggregate in tools like Elasticsearch, Loki, or Datadog.
Multi-Tenant API Gateway
Use the NGINX JSON module to route requests to different backends based on JSON metadata:
map $tenant_name {
default backend_default;
acme backend_acme;
globex backend_globex;
}
upstream backend_default { server 10.0.1.10:8080; }
upstream backend_acme { server 10.0.2.10:8080; }
upstream backend_globex { server 10.0.3.10:8080; }
server {
listen 80;
location /api/ {
json_loads $json $http_x_tenant_config;
json_dumps $tenant_name $json name;
json_dumps $tenant_tier $json tier;
proxy_set_header X-Tenant-Name $tenant_name;
proxy_set_header X-Tenant-Tier $tenant_tier;
proxy_pass http://$tenant_name;
}
}
Clients include their tenant configuration as a JSON header:
curl -H 'X-Tenant-Config: {"name":"acme","tier":"premium"}' \
https://gateway.example.com/api/orders
NGINX extracts the tenant name, selects the correct upstream, and forwards the request — all without touching your application code.
Important Limitations
URL-Encoded Variables Cannot Be Parsed Directly
NGINX does not URL-decode $arg_* variables automatically. If you pass JSON as a query parameter, the value arrives URL-encoded (%7B%22key%22...), and json_loads will fail with an error like:
!json_loadb: invalid token near '%'
To work around this, use the NGINX Set-Misc module’s set_unescape_uri directive to decode the value first:
set_unescape_uri $decoded_json $arg_data;
json_loads $json $decoded_json;
json_dumps $value $json key;
Alternatively, pass JSON through HTTP headers instead of query parameters, since header values are not URL-encoded.
Invalid JSON Returns 500
If json_loads receives invalid JSON, NGINX returns a 500 Internal Server Error and logs the parsing error. There is no way to gracefully handle parse failures within the NGINX configuration alone. Therefore, make sure the JSON source is reliable before parsing it.
Non-String Values
When json_dumps navigates to a non-string value (a number, boolean, null, or nested object), it returns the JSON representation as a string. For example, true is returned as the literal string true, and 42 is returned as 42. This is typically fine for logging and header forwarding, but be aware that you are working with string representations, not typed values.
Comparison with Alternatives
The NGINX JSON module is not the only way to handle JSON in NGINX. Here is how it compares with other approaches:
| Feature | JSON Module | njs (JavaScript) | Lua (OpenResty) |
|---|---|---|---|
| Complexity | Two directives | Full JavaScript runtime | Full Lua runtime |
| Learning curve | Minimal | Moderate | Moderate |
| JSON parsing | Fields only | Full JSON.parse() |
Full cjson.decode() |
| JSON creation | No | Yes (JSON.stringify()) |
Yes (cjson.encode()) |
| Conditionals | No (use map) |
Yes | Yes |
| Memory footprint | Very small | ~2 MB per worker | ~2 MB per worker |
| Best for | Field extraction | Complex logic | Complex logic |
Choose the NGINX JSON module when you need to extract a few fields from JSON data and use them in directives like proxy_set_header, log_format, or map. It requires no scripting knowledge and adds virtually no overhead.
Choose njs or Lua when you need to create JSON, modify JSON values, implement conditional logic based on JSON content, or perform complex data transformations.
Related Modules
The NGINX JSON module works well alongside other NDK-based modules from the GetPageSpeed repository:
- Set-Misc module — URL decoding, base64, hashing, and other string operations. Use
set_unescape_uribeforejson_loadswhen parsing URL-encoded JSON. - Form Input module — Parses POST form data into NGINX variables, similar to how the JSON module parses JSON data.
- Encrypted Session module — AES-256 encryption for NGINX variables. Combine with the JSON module to encrypt extracted JSON fields before forwarding.
- Array Variables module — Split, map, and join array data in NGINX variables.
Performance Considerations
The NGINX JSON module uses the jansson library, which is a well-optimized C library for JSON parsing. The performance characteristics are:
- Parsing is lazy: JSON is only parsed when a
json_dumpsvariable is actually evaluated. If a request path does not access any JSON variables, no parsing occurs. - Parsing happens once: The JSON string is parsed on the first
json_dumpsevaluation. Subsequentjson_dumpscalls on the same JSON variable reuse the already-parsed structure. - Memory is pool-allocated: Parsed JSON data is allocated from the NGINX request pool and freed automatically when the request completes. There are no memory leaks.
- No I/O overhead: Unlike Lua or njs, the JSON module does not load an interpreter or execute scripts. It operates as a native C function call within the NGINX request processing pipeline.
For most use cases — parsing a small JSON header or config string per request — the overhead is negligible compared to the network latency of the proxied request itself.
Troubleshooting
“unknown directive json_loads”
The module is not loaded. Verify that both load_module lines are present at the very top of /etc/nginx/nginx.conf:
load_module modules/ndk_http_module.so;
load_module modules/ngx_http_json_module.so;
Also verify that the module files exist:
ls /usr/lib64/nginx/modules/ndk_http_module.so
ls /usr/lib64/nginx/modules/ngx_http_json_module.so
“module is not binary compatible”
This means the module was compiled for a different NGINX version than the one you are running. Ensure both nginx and nginx-module-json are from the same repository:
rpm -q nginx nginx-module-json nginx-module-ndk
All three packages should show the same NGINX version number (for example, 1.28.2).
“!json_loadb: invalid token” in error log
The input string is not valid JSON. Common causes include:
- URL-encoded values:
$arg_*variables are URL-encoded. Useset_unescape_urito decode first. - Empty variables: The source variable is empty or undefined. Add error handling in your application to ensure the header or variable always contains valid JSON.
- Escaped quotes: The JSON string may have been double-escaped. Check the raw value by logging it before parsing.
“!json_string_value” warnings in error log
This warning appears when json_dumps navigates to a non-string JSON value (number, boolean, null, object, or array). The module falls back to json_dumps() and outputs the JSON representation as a string. While this works correctly, the warning indicates you are accessing a non-string field. This is normal behavior and can be safely ignored.
Conclusion
The NGINX JSON module provides a lightweight, zero-overhead way to parse and extract JSON data in your NGINX configuration. With just two directives — json_loads and json_dumps — you can extract fields from JSON headers, enrich your access logs with structured data, and build multi-tenant routing logic without any scripting.
For sysadmins who need basic JSON field extraction at the NGINX layer, this module is far simpler than deploying a full Lua or njs runtime. Install it from the GetPageSpeed RPM repository or the APT repository, and start using JSON data in your NGINX configs today.
The module source code is available on GitHub.
