Site icon GetPageSpeed

NGINX JSON Module: Parse and Extract JSON Data

NGINX JSON Module: Parse and Extract JSON Data in Your Server Config

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:

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.

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.

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.

The NGINX JSON module works well alongside other NDK-based modules from the GetPageSpeed repository:

Performance Considerations

The NGINX JSON module uses the jansson library, which is a well-optimized C library for JSON parsing. The performance characteristics are:

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:

“!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.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Exit mobile version