Site icon GetPageSpeed

NGINX Form Input Module: Parse POST Data into Variables

NGINX Form Input Module: Parse POST Data into Variables

The NGINX form input module lets you parse HTTP POST and PUT request bodies encoded as application/x-www-form-urlencoded and extract individual form fields into NGINX variables. This gives system administrators the power to make routing decisions, log specific form values, and set proxy headers based on POST data — all at the NGINX level, before the request ever reaches a backend application.

In this guide, you will learn how to install and configure the module, understand its directives and limitations, and apply it to real-world use cases.

How It Works

When a client submits an HTML form via POST or PUT, the browser typically encodes the data as application/x-www-form-urlencoded. For example, a login form submission produces a request body like:

username=admin&password=secret123&action=login

The module intercepts this request body during the rewrite phase and parses it into key-value pairs. You can then access any form field as an NGINX variable using the set_form_input directive.

This module depends on the NGINX Development Kit (NDK), which provides the underlying variable-setting infrastructure. Both modules must be loaded for form-input to function.

Key Characteristics

Installation

RHEL, CentOS, AlmaLinux, Rocky Linux

sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-form-input

The package automatically installs the required nginx-module-ndk dependency. After installation, load both modules in your nginx.conf. The NDK module must be loaded first:

load_module modules/ndk_http_module.so;
load_module modules/ngx_http_form_input_module.so;

Debian and Ubuntu

First, set up the GetPageSpeed APT repository, then install:

sudo apt-get update
sudo apt-get install nginx-module-form-input

On Debian/Ubuntu, the package handles module loading automatically. No load_module directive is needed.

You can find additional package details on the RPM module page and APT module page.

Directives

The NGINX form input module provides two directives. Both work in http, server, and location contexts.

set_form_input

set_form_input $variable [field_name];

Parses a single form field from the request body and stores its value in $variable.

Example:

location /submit {
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    # Variable name matches field name
    set_form_input $name;

    # Variable name differs from field name
    set_form_input $user_email email;

    return 200 "name=$name, email=$user_email\n";
}

Sending a POST request:

curl -X POST http://localhost/submit -d "name=John&email=john@example.com"

Returns:

name=John, email=john@example.com

set_form_input_multi

set_form_input_multi $variable [field_name];

Parses all values for a given field name into an array variable. This is useful when a form submits multiple values with the same name, such as checkboxes or multi-select lists.

The resulting array requires the NGINX array-var module to manipulate. Use array_join to convert the array into a delimited string.

Example with the array-var module:

location /preferences {
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    set_form_input_multi $colors color;
    array_join ", " $colors;

    return 200 "Selected colors: $colors\n";
}

Critical Configuration: Buffer Size Alignment

The module reads the request body from in-memory buffers only. If the body exceeds client_body_buffer_size, NGINX writes the overflow to a temporary disk file. The module cannot read disk-buffered data.

You must set client_max_body_size and client_body_buffer_size to the same value:

location /api {
    # Both must match — disk-buffered bodies cannot be parsed
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    set_form_input $action;
    # ...
}

If the body exceeds the buffer, NGINX logs an error:

form-input: in-file buffer found. aborted. consider increasing your client_body_buffer_size setting

For more details on body size settings, see our NGINX client_max_body_size guide.

Practical Use Cases

Conditional Routing Based on Form Data

Route POST requests to different handlers based on a field value. This eliminates the need for a backend to handle simple routing:

location /api {
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    set_form_input $action;

    if ($action = "login") {
        return 200 "Routing to login handler\n";
    }
    if ($action = "register") {
        return 200 "Routing to registration handler\n";
    }
    return 400 "Unknown action\n";
}

For advanced variable-based routing, combine set_form_input with the map directive to map field values to upstream backends.

Logging Form Field Values

Capture specific fields in NGINX access logs for auditing or analytics:

log_format form_log '$remote_addr - [$time_local] "$request" '
                    '$status - username=$form_username';

server {
    location /login {
        client_max_body_size 100k;
        client_body_buffer_size 100k;

        set_form_input $form_username username;

        access_log /var/log/nginx/login.log form_log;
        proxy_pass http://backend;
    }
}

A POST to /login with username=johndoe produces:

127.0.0.1 - [06/Mar/2026:12:28:19 +0800] "POST /login HTTP/1.1" 200 - username=johndoe

Security note: Never log sensitive fields like passwords. Only log identifiers needed for auditing.

Setting Proxy Headers from Form Data

Extract a form field and pass it as a custom header to the backend:

location /api/submit {
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    set_form_input $token;

    proxy_set_header X-Form-Token $token;
    proxy_pass http://backend;
}

For a comprehensive reverse proxy guide, see NGINX Reverse Proxy.

URL-Decoding Form Values

Raw URL-encoded values are returned by default. Characters like %20 (space) and %40 (@) stay encoded. The + character (space in form encoding) is also preserved as-is.

To decode these values, use set_unescape_uri from the set-misc module:

location /search {
    client_max_body_size 100k;
    client_body_buffer_size 100k;

    set_form_input $raw_query query;
    set_unescape_uri $decoded_query $raw_query;

    # $raw_query: "hello%20world%21"
    # $decoded_query: "hello world!"

    return 200 "Search query: $decoded_query\n";
}

The set-misc module also depends on NDK. Load all three modules together:

load_module modules/ndk_http_module.so;
load_module modules/ngx_http_set_misc_module.so;
load_module modules/ngx_http_form_input_module.so;

Testing Your Configuration

Syntax Check

nginx -t

Runtime Verification

Test with a simple POST request:

curl -X POST http://localhost/submit \
    -d "name=TestUser&email=test@example.com"

Verify Content-Type Filtering

JSON payloads are ignored — only URL-encoded forms are parsed:

# Returns empty variables — JSON is not parsed
curl -X POST http://localhost/submit \
    -H "Content-Type: application/json" \
    -d '{"name":"TestUser"}'

Verify GET Requests Are Ignored

# GET requests produce empty variables
curl http://localhost/submit

Verify PUT Support

# PUT requests work just like POST
curl -X PUT http://localhost/submit \
    -d "name=PutUser&email=put@example.com"

Performance Considerations

The module has minimal performance impact. It operates only during the rewrite phase and only on matching requests:

Security Best Practices

Troubleshooting

Variables Are Always Empty

  1. Check the HTTP method: Only POST and PUT are supported.
  2. Check the Content-Type: Must be application/x-www-form-urlencoded.
  3. Verify module loading order: NDK must load before form-input.
  4. Check buffer sizes: The buffer must hold the entire body.

“in-file buffer found” Error

The request body exceeded client_body_buffer_size. Set both directives to the same value:

client_max_body_size 100k;
client_body_buffer_size 100k;

“unknown directive” Error

The module is not loaded. Verify:

  1. The .so file exists: ls /usr/lib64/nginx/modules/ngx_http_form_input_module.so
  2. Both load_module lines are at the top of nginx.conf, before events
  3. NDK is loaded before form-input

Alternatives

Depending on your needs, other approaches may fit better:

The NGINX form input module is the right choice for simple, declarative extraction of URL-encoded form fields without a scripting engine.

Conclusion

The NGINX form input module bridges NGINX’s request routing with the POST data that applications submit via HTML forms. By extracting form fields into NGINX variables, you can implement conditional routing, targeted logging, and proxy header injection — without modifying backend code.

Combined with set-misc for URL decoding and array-var for multi-value fields, form-input becomes a versatile tool for handling form submissions at the edge.

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