NGINX Unit

Scripting§

NGINX Unit’s control API supports JavaScript expressions, including function calls, in the form of template literals written in NGINX JavaScript ( njs ). They can be used with these configuration options:

As its JavaScript engine, Unit uses the njs library, shipped with the official packages or built from source.

Warning

Unit 1.32.0 and later require njs 0.8.2.

Some request properties are exposed as njs objects or scalars:

Name Type Description
args Object Query string arguments; Color=Blue is args.Color; can be used with Object.keys().
cookies Object Request cookies; an authID cookie is cookies.authID; can be used with Object.keys().
headers Object Request header fields; Accept is headers.Accept, Content-Encoding is headers[‘Content-Encoding’] (hyphen requires an array property accessor); can be used with Object.keys().
host Scalar Host header field, converted to lower case and normalized by removing the port number and the trailing period (if any).
remoteAddr Scalar Remote IP address of the request.
uri Scalar Request target, percent decoded and normalized by removing the query string and resolving relative references (“.” and “..”, “//”).
vars Object Unit variables; vars.method is $method.

Template literals are wrapped in backticks. To use a literal backtick in a string, escape it: \\` (escaping backslashes is a JSON requirement). The njs snippets should be enclosed in curly brackets: ${…}.

Next, you can upload and use custom JavaScript modules with your configuration. Consider this http.js script that distinguishes requests by their Authorization header field values:

var http = {}

http.route = function(headers) {
    var authorization = headers['Authorization'];
    if (authorization) {
        var user = atob(authorization.split(' ')[1]);
        if (String(user) == 'user:password') {
            return 'accept';
        }

        return 'forbidden';
    }

    return 'unauthorized';
}

export default http

To upload it to Unit’s JavaScript module storage as http:

# curl -X PUT --data-binary @http.js --unix-socket /path/to/control.unit.sock \
      http://localhost/js_modules/http

Unit doesn’t enable the uploaded modules by default, so add the module’s name to settings/js_module:

# curl -X PUT -d '"http"' /path/to/control.unit.sock \
      http://localhost/config/settings/js_module

Note

Please note that the js_module option can be a string or an array; choose the appropriate HTTP method.

Now, the http.route() function can be used with Unit-supplied header field values:

{
    "routes": {
        "entry": [
            {
                "action": {
                    "pass": "`routes/${http.route(headers)}`"
                }
            }
        ],

        "unauthorized": [
            {
                "action": {
                    "return": 401
                }
            }
        ],

        "forbidden": [
            {
                "action": {
                    "return": 403
                }
            }
        ],

        "accept": [
            {
                "action": {
                    "return": 204
                }
            }
        ]
    }
}

Examples§

This example adds simple routing logic that extracts the agent name from the User-Agent header field to reject requests issued by curl:

"routes": {
    "parse": [
        {
            "action": {
                "pass": "`routes/${ headers['User-Agent'].split('/')[0] == 'curl' ? 'reject' : 'default' }`"
            }
        }
    ],

    "reject": [
        {
            "action": {
                "return": 400
            }
        }
    ],

    "default": [
        {
            "action": {
                "return": 204
            }
        }
    ]
}

This example uses a series of transformations to log the request’s date, IP, URI, and all its headers:

{
    "path": "/var/log/unit/access_kv.log",
    "format": "`@timestamp=${new Date().toISOString()} ip=${remoteAddr} uri=${uri} ${Object.keys(headers).map(k => 'req.' + k + '=\"' + headers[k] + '\"').join(' ')}\n`"
}

The next example will add the Cache-Control Header based on the HTTP Request method:

{
    "action": {
      "pass": "applications/my_app",
      "response_headers": {
         "Cache-Control": "`${vars.method.startsWith('P') ? 'no-cache' : 'max-age=3600'}`"
      }
    }
}

For further reference, see the njs documentation.