Skip to main content

NGINX

NGINX JSON Module: Parse and Extract JSON Data

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth 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 variable
  • json_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.

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:

  • Set-Misc moduleURL decoding, base64, hashing, and other string operations. Use set_unescape_uri before json_loads when 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_dumps variable 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_dumps evaluation. Subsequent json_dumps calls 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. Use set_unescape_uri to 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.

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

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.