NGINX 1.31.4: PROXY Protocol v2, Stricter Validation and More

by

in ,
Man standing in front of presentation screen pointing to images of squares with woman holding a laptop next to the screen

NGINX 1.31.4 is an incremental release focused on the protocols we all depend on. Backends behind a stream or mail proxy can now see a client’s TLS details alongside its address thanks to PROXY protocol v2, HTTP/2 and gRPC requests to your upstreams follow the standards more closely, and NGINX has stricter checks on malformed traffic. For NGINX developers out there, there’s also new groundwork for structured data output, and a compatibility fix for a small portion of third-party modules. Here’s what’s new!

PROXY protocol v2 to upstreams

What’s new?: The stream and mail proxy modules can now speak PROXY protocol version 2 to your upstreams. Use proxy_protocol v2; to opt in. Version 1 remains the default. (nginx/nginx#1204)

When traffic passes through a load balancer or proxy, the backend server typically only sees the proxy’s IP address. The PROXY protocol solves this “blind spot” by safely passing the original client’s connection details (like source IP and port) to the backend. While v1 accomplished this using a plain-text string, PROXY Protocol v2 introduces a highly optimized, fixed-size binary format. By moving to this binary format, NGINX can now securely carry extensible TLV (Type-Length-Value) metadata to your backends.

Why it matters: Version 1 already gave your backends the client’s address. Version 2 adds the negotiated protocol and the client certificate identity, with no side channel or custom header scheme. That’s what you need when you delegate part of your traffic management (e.g. access control, rate limiting, or audit logging) to a service behind NGINX. Version 2 also lets you interoperate with managed cloud load balancers and private endpoint services that require PROXY protocol v2, while expanding support to UDP datagrams to let you securely proxy real-time gaming, IoT, and custom Layer 4 workloads.

Standards-correct host handling for HTTP/2 and gRPC upstreams

What’s new?: HTTP/2 and gRPC requests to your upstreams now always carry the :authority pseudo-header, and the Host header is no longer sent. If you set a host explicitly with proxy_set_header Host or grpc_set_header Host, that value now goes into :authority, where the upstream expects it. Across HTTP/1.1, HTTP/2, and gRPC alike, an explicitly empty host now falls back to $proxy_host instead of going out empty. (nginx/nginx#1593)

Why it matters: The HTTP/2 and HTTP semantics standards (RFC 9113 and RFC 9110) define that authority information belongs in :authority, and that a host value can’t be empty. Until now, if you overrode the upstream host on an HTTP/2 or gRPC proxy, you had no supported way to reach :authority. The value went into Host instead, and strict upstreams and service meshes rejected the request.

Stricter checks on malformed traffic

What’s new?: Two validation fixes, one on flow control, one on response length, tighten what NGINX accepts from clients and upstreams, so protocol violations fail visibly instead of passing as success:

  • QUIC connections now apply per-stream flow control to the final size a client reports when it resets a stream, and that final-size check runs before the checks that could previously skip it. Reset stream sizes are also logged in decimal rather than hex. (nginx/nginx#1612)
  • A gRPC upstream that ends a response immediately while still declaring a non-zero length is now caught by the existing response-length check and treated as incomplete, returning a 502 rather than handing the client an empty 200. (nginx/nginx#1591)

Why it matters: Silent successes on malformed inputs are the hardest class of bug to catch in production. An upstream or client that breaks the protocol should fail loudly.

Stability and memory-safety fixes

What’s new?: Four bugs that could crash a worker or read uninitialized memory are now fixed:

  • The image filter module treated its buffer size as the amount of data actually received, so a truncated upstream response without a content length could be decoded past the real data. (nginx/nginx#1574)
  • Overflow detection in the chunked parser, broken in 1.29.4, works again. (nginx/nginx#1625)
  • A worker using the select event method could write out of bounds and crash when the operating system handed it a high file descriptor. That descriptor is now validated at runtime. (nginx/nginx#1598)
  • An allocation failure in the XSLT filter module left a pointer unset that the error path then used. That cleanup path is now guarded. (nginx/nginx#1565)

Why it matters: You don’t need a malicious client for any of these to cause trouble. A truncated response from an upstream, a failed memory allocation, or a worker with a lot of files open could be enough. These fixes ensure NGINX keeps running smoothly.

New code foundations for structured data

What’s new?: Two new core libraries land in this release. ngx_data describes structured data as a tree of typed items (objects, lists, strings, integers, booleans, and null), with declarative handlers to populate that tree from NGINX’s internal structures. ngx_json renders the tree as JSON. (nginx/nginx#1569)

Why it matters: These are libraries for NGINX developers rather than something you might configure. Until now, a module that needed to output structured data wrote both the data handling and the rendering by hand. Now ngx_data describes the data and ngx_json renders it, so modules share that work rather than duplicating it, and the data model stays independent of the output format.

Third-party module compatibility

What’s new?: Third-party modules that reuse NGINX’s complex value evaluation work again. An earlier release split that evaluation into two steps, which inadvertently broke modules that only include the first. One caveat: those modules no longer break outright, but until they are updated they risk reading uninitialized memory instead. (nginx/nginx#1601)

Why it matters: If you run a third-party module that broke, upgrading to 1.31.4 gets it working again without waiting on the maintainer. If you maintain one, here’s the fix: wherever your module adds ngx_http_script_complex_value_code to its compiled script, add ngx_http_script_complex_value_end_code after it.

Bug fixes and smaller enhancements

This release also includes a number of smaller improvements:

  • Upstream state padding: Reserved padding added to the upstream state structures. (nginx/nginx#1631)
  • Repository improvements:
    • Security policy: Updated with current reporting methods. (nginx/nginx#1586)
    • Windows build fix: Failing Microsoft Visual C++ buildbot jobs are fixed across the three modules sharing the pattern — proxy, gRPC, and proxy v2. (nginx/nginx#1635)

Community recognition

Starting with this release, we want to start giving more recognition to all our amazing community members that have contributed to this project in this release. Whether you contributed code, helped shape a feature, or reported major bugs, thank you!

Community contributions

Here are the community members (now NGINX contributors!) whose work landed in NGINX since the last release. Between them, they fixed three bugs, in protocol validation, memory safety, and error logging; improved how NGINX uses memory, and how the project runs its CI. In alphabetical order, these are:

  • devnexen: Restored the read-only safeguard on the Perl module’s zero-copy path, closing a window where a script that modified a scalar after printing it could corrupt the response or send freed memory to the client. (nginx/nginx#1581)
  • felix314159: Made zero-increment flow-control updates an error on HTTP/2 and gRPC upstream connections, which the standard requires. They were previously accepted and the response forwarded as successful. (nginx/nginx#1622)
  • nishat-06: Fixed format specifier mismatches in error logging. A missing argument in the Encrypted Client Hello key loading code could crash config load or copy unrelated memory into the log, and a follow-up sweep caught the same problem in QUIC frame logs, geo range overlap messages, and several config errors. (nginx/nginx#1592)
  • Srujan-rai: Moved autoindex directory entries to a temporary pool that’s destroyed as soon as the response buffer is complete, on every path including errors. (nginx/nginx#1311)

Community inspired development work

The PROXY protocol v2 upstream support was inspired by two prior efforts, both credited in the PR: a commit in the Angie project, and mdewitt11‘s earlier implementation. Thanks to the Angie team and mdewitt11!

Community reports

Thanks to the researchers and users who reported issues fixed in this release: Matt Suiche (Tolmo Inc.) for the chunked parser overflow detection regression, and Tony Wang for the QUIC stream reset flow control issue.


Head over to the GitHub release page and the CHANGES file for the full picture, and the NGINX documentation for details on the directives mentioned here!

We’re proud of what the team put together, and we can’t wait to share it with the community!

NGINX Community Forum