Web Server Load Balancing with NGINX Plus

We’re happy to announce the availability of NGINX Plus Release 26 (R26). Based on NGINX Open Source, NGINX Plus is the only all-in-one software web server, load balancer, reverse proxy, content cache, and API gateway.

New and enhanced features in NGINX Plus R26 include:

  • Faster JWT validation with JSON Web Key Set caching – Continuing the series of enhancements to JSON Web Tokens (JWT) support added over the last few releases, we introduce in‑memory caching of JSON Web Key Sets (JWKS), which substantially reduces overhead for JWT validation.
  • Hardened TLS handshakes – NGINX Plus rejects the TLS handshake if the client proposes a communication protocol via ALPN that doesn’t match the NGINX configuration context for the session being established (for example proposes IMAP to a virtual server in the http{} context).
  • Enhancements to the NGINX JavaScript module – Asynchronous functions that use the async and await keywords and Promise object are now supported, and we have implemented the WebCrypto API for cryptographic operations (like generating random numbers or encrypting cookies).

Rounding out this release are support for the IBM System Z (s390x) architecture, the ability to close each direction of a TCP connection independently, and support for version 2 of the Perl Compatible Regular Expression (PCRE) library.

Important Changes in Behavior

NGINX JavaScript Module No Longer Supports js_include

As announced at the release of NGINX Plus R23, in version 0.4.0 of the NGINX JavaScript module the js_import directive replaced the js_include directive. The js_include directive was deprecated at that time and as of this release is no longer supported.

Before upgrading to NGINX Plus R26, replace js_include with js_import in NGINX configuration files and also add an export statement to JavaScript files for functions that are referenced in NGINX configuration. Follow these steps:

  1. Edit NGINX configuration files:

    • Replace js_include with js_import and make a note of the implicit module_name (the JavaScript filename parameter to the directive, without the .js extension).

    • In each directive that references a JavaScript function, prefix the function name with the module_name. The function name is the first parameter to these directives:

      It is the second parameter to the js_set[HTTP][Stream] directive.

    For example, change:

    js_set $foo myFunction;


    js_set $foo module_name.myFunction;
  2. Edit the JavaScript (module_name.js) files that define functions referenced in an NGINX configuration file. Add an export statement like the following to each file, naming the referenced functions.

    export default { myFunction, otherFunction }

    The export statement can appear anywhere in the .js file, but by convention it is placed either directly above the functions or at the end of the file.

Cookie-Flag Module Is Obsolete

The third‑party Cookie‑Flag module was deprecated in NGINX Plus R23 and as announced at that time is no longer available in the NGINX modules repository as of this release.

Before upgrading to NGINX Plus R26, edit your NGINX configuration to replace any occurrences of the set_cookie_flag directive (defined in the deprecated module) with the built‑in proxy_cookie_flags directive.

TLS Negotiation No Longer Supports the NPN Protocol

The way that NGINX establishes TLS and HTTP/2 connections has been updated. As part of the TLS handshake between NGINX and a client (usually a browser), they negotiate which communication protocol will be used in the session established by the handshake (most often, the negotiation upgrades the session from HTTP 1.x to HTTP/2). The Next Protocol Negotiation (NPN) extension to TLS was the first method used for this purpose, but NPN is now considered obsolete and is superseded by the Application‑Layer Protocol Negotiation (ALPN) extension, published as RFC 7301.

NGINX Plus R26 no longer supports NPN, so clients must now use ALPN exclusively.

In addition, our ALPN implementation has been extended and hardened – see Hardened TLS Handshakes.

The Old NGINX Plus Software Repository Is No Longer Updated

At the release of NGINX Plus R24, the package repositories for all NGINX software were reorganized, resulting in changes to the NGINX Plus installation procedure.

When you install or upgrade NGINX Plus, the operating system’s package manager (apt, yum, or equivalent) is configured with the software repository for NGINX Plus.

If upgrading to NGINX Plus R26 on an existing system configured to use the old repo (those running NGINX Plus R23 or earlier), you must update the package manager to refer to the new repo. See the instructions in the F5 Knowledge Base.

If performing an initial installation of NGINX Plus R26, see Installing NGINX Plus in the NGINX Plus Admin Guide.

NGINX Plus R26 is not available in the old repository, which will not receive further updates.

Changed Access to the OpenAPI Spec for the NGINX Plus API

The NGINX Plus software package no longer includes the YAML‑format OpenAPI specification and Swagger UI for the NGINX Plus API. You can now access them in the NGINX Plus Admin Guide.

Changes to Platform Support

New operating systems and architectures supported:

Older operating systems removed:

  • Alpine Linux 3.11 (oldest supported version is 3.12)

Older operating systems and architectures deprecated and scheduled for removal in NGINX Plus R27:

  • Power8 architecture (ppc64le)
  • CentOS 8.1+
  • Alpine Linux 3.12

New Features in Detail

Faster JWT Validation with JSON Web Key Set Caching

When validating JSON Web Tokens, NGINX uses a JSON Web Key Set (JWKS) to verify the token’s signature or decrypt the token. JWKSs can either be stored in configuration files or obtained from external services via an HTTP request. Additionally caching a JWKS in memory has several benefits:

  • Significant reduction in CPU usage
  • Reduced request latency
  • Streamlining of JWT validation, because as part of the caching process JWKS keys are converted from JSON into a binary format that is optimized for cryptographic operations

To cache JWKSs in memory, include the new auth_jwt_key_cache directive and specify the expiration time for each key set (in this example, 3 hours):

When JWKs are obtained from an external server, we also recommend configuring standard content caching and including the proxy_cache_use_stale directive, which tells NGINX Plus to continue serving an expired JWKS while it’s being refreshed in the background.

The benefits of content caching in addition to JWKS caching are twofold:

  • Resiliency – The JWKS can be retrieved from the cache even when it has expired. This increases resiliency when the JWKS provider is temporarily unavailable, but there is a tradeoff of increased security risk.

  • Effect on the authorization server – Expiration of a cached JWKS affects the auth server differently depending on whether JWKS caching is used alone or in combination with content caching:

    • With JWKS caching alone, all incoming authorization requests are forwarded to the auth server until the cache is repopulated with a new version of the expired JWKS. If the auth server responds slowly, there can be a sudden increase in repeated HTTP requests for the JWKS. This extra load might overwhelm the auth service, making the problem worse.

    • When content caching is enabled with serving of expired JWKSs, only one request for the JWKS is forwarded to the auth server, with subsequent requests queued until NGINX can satisfy them after the content cache is populated. This results in lower demand (and thus lower resource consumption) on the auth service.

Hardened TLS Handshakes

Attacks against TLS, such as ALPACA, are increasing. As part of our ongoing commitment to proactively defending against exploits, we have hardened NGINX’s handling of TLS connections.

Application‑Layer Protocol Negotiation (ALPN) is an optional extension to the TLS handshake, used by the client and server during the TLS handshake to choose the Layer 7 protocol they will use in the encrypted session established by the handshake. The most common use case for ALPN is to negotiate the upgrade from HTTP/1.x to HTTP/2 for the session between a browser and a web or app server.

NGINX Plus now rejects a TLS handshake if the client proposes a protocol via ALPN that doesn’t match the NGINX configuration context of the session being established. For example, a virtual server defined in the http{} context requires an ALPN protocol ID for HTTP, while a virtual server in the mail{} context requires a protocol ID for SMTP, POP, or IMAP.

NGINX Plus R26 introduces the $ssl_alpn_protocol[HTTP][Stream] variable to capture the negotiated protocol. (The $ssl_preread_alpn_protocols variable introduced in the stream{} context in NGINX Plus R15 still captures the list of all protocols advertised by the client during the handshake.)

This snippet defines the alpn log format which uses $ssl_alpn_protocol to include the protocol in the alpn= field of entries in the access log.

The new ssl_alpn directive in the stream{} context defines which protocols NGINX Plus accepts. Omit the directive to enable NGINX Plus to consider all protocols presented by the client.

NGINX JavaScript Module Enhancements

NGINX Plus R26 incorporates version 0.7.2 of the NGINX JavaScript module (njs) and includes two enhancements:

Note: This section assumes you understand the JavaScript constructs for asynchronous and cryptographic operations. A full analysis of the code snippets is outside the scope of this blog.

[Editor – The use cases described in this section are just some of the many use cases for the NGINX JavaScript module. For a complete list, see Use Cases for the NGINX JavaScript Module.]

Enhanced Support for Asynchronous Functions

In many commonly used scripting languages like PHP, commands and functions execute synchronously – that is, after a script invokes a function it pauses (stops executing) until the function returns a result.

JavaScript can also operate asynchronously: when a function is invoked asynchronously the script continues executing without waiting for the result to return from the function.

Take this sample script:

It returns an empty response because the njs runtime does not wait for the defined timeouts to elapse (if it did wait, the output would be b,a):

$ curl

Handling asynchronous operations correctly is obviously crucial to getting the intended result. JavaScript provides a number of ways to do this, but in the common NGINX use cases, it’s often desirable simply to wrap an asynchronous function in a way that makes the execution flow synchronous. This is where the Promise object and async and await keywords come into play.

ECMAScript 6 (the sixth edition of the ECMA‑262 language specification for JavasScript) defines the Promise object as a return type for asynchronous functions. It exists in one of three states:

  • fulfilled – The operation completed successfully
  • rejected – The operation failed
  • pending – The initial state (neither fulfilled or rejected)

Defining a JavaScript function with the keyword async sets the function’s return type to Promise. The async and await keywords are important when you are writing njs functions dealing with Promise objects.

Take this example:

The fs.readFile function (line 12) returns a Promise. It is wrapped in a custom async function that ensures that fs.readFile() is invoked only if the file is called user.text. Because of the await keyword, the wrapping function then waits for the Promise and returns the data.

Wrapping fs.readFile() in another function makes it easier to catch errors; any exception in the async function sets the state of the Promise to rejected. Another way to do this is to replace line 9 with a statement that returns a rejected Promise:

You can also work directly with Promise objects. In the following example, the Promise.resolve functions return a Promise for each of p1 and p2. The Promise.all function waits for the promises for both p1 and p2 to be resolved before returning a result.

Now the output from our curl command is what we want (note that b is retuned first due to the shorter timeout value):

$ curl

New Cryptographic Functions with the WebCrypto API

NGINX JavaScript now has access to enhanced cryptographic capabilities via the WebCrypto API. Common njs cryptographic use cases include:

  • Generating secure random numbers for session IDs
  • Encrypting and decrypting messages, data, and cookies
  • Creating or validating digital signatures using symmetric as well as asymmetric crypto algorithms

This njs code generates a random number:

And this NGINX Plus configuration invokes the njs code:

The output of the function is a random number something like this:

$ curl

The getRandomValues function in WebCrypto is a great entry point to get started with secure random numbers and WebCrypto in general. Its implementation is quite simple, and the function returns results directly, as opposed to returning a Promise.

Some of the other more intensive WebCrypto cryptographic functions operate asynchronously, however. For example, the documentation for сrypto.subtle.digest() states:

Generates a digest of the given data. Takes as its arguments an identifier for the digest algorithm to use and the data to digest. Returns a Promise which will be fulfilled with the digest.

Calling сrypto.subtle.digest() directly, therefore, does not guarantee that its result will be available to the next step, unless it’s wrapped in an async function. So here we wrap it in a function with the async and the await keywords to ensure that the hash variable is populated with a result before the function returns:

The js_set directive in this NGINX Plus configuration populates the $hosthash variable with the value returned by the setReturnValue function (as wrapped in the host_hash function):

Here’s an example that hashes the hostname

$ curl -H "Host:" 127.1

Other Enhancements in NGINX Plus R26

Support for the IBM Z (s390x) Architecture

As modern applications colonize every available digital biome, it’s important that the essential life‑support components – like NGINX – travel with them, so we’re pleased to support NGINX Plus on the IBM Z (s390x) architecture with CentOS 8.1+, RHEL 8.1+, and Ubuntu 20.04. Organizations looking to host modern applications on their existing mainframe assets can now deploy NGINX and NGINX Plus as a software‑based web server, load balancer, reverse proxy, content cache, and API gateway.

TCP Half-Close Support in the Stream Module

The new proxy_half_close directive enables independent closure of each direction of a TCP connection, for extra efficiency in stream{} contexts.

PCRE2 Library Support

Previous versions of NGINX Plus use the Perl Compatible Regular Expression (PCRE) library (version 1) to evaluate regular expressions used in NGINX configuration. This significant open source project has recently reached end of life, superseded by PCRE2. NGINX Plus is built with PCRE2, but it supports dynamic modules built with both PCRE and PCRE2, using the version available with the underlying operating system. No configuration changes are required.

Upgrade or Try NGINX Plus

If you’re running NGINX Plus, we strongly encourage you to upgrade to NGINX Plus R26 as soon as possible. You’ll also pick up several additional fixes and improvements, and it will help NGINX to help you when you need to raise a support ticket.

If you haven’t tried NGINX Plus, we encourage you to try it out – for security, load balancing, and API gateway, or as a fully supported web server with enhanced monitoring and management APIs. You can get started today with a free 30-day trial.

Hero image
Free O'Reilly eBook: The Complete NGINX Cookbook

Updated for 2022 – Your Guide to Everything NGINX

About The Author

Robert Haynes

Technical Marketing Manager

About F5 NGINX

F5, Inc. is the company behind NGINX, the popular open source project. We offer a suite of technologies for developing and delivering modern applications. Together with F5, our combined solution bridges the gap between NetOps and DevOps, with multi-cloud application services that span from code to customer.

Learn more at or join the conversation by following @nginx on Twitter.