Scripting§
NGINX Unit’s control API supports JavaScript expressions, including function calls, in the form of template literals written in the njs dialect of JavaScript. They can be used with these configuration options:
pass
in listeners and actions to choose between routes, applications, app targets, or upstreams.response_headers
values in actions to manipulate response header fields.rewrite
in actions to enable URI rewriting.share
andchroot
in actions to control static content serving.location
inreturn
actions to enable HTTP redirects.format
in the access log to customize Unit’s log output.
As its JavaScript engine, Unit uses the njs library, shipped with the official packages or built from source.
Warning
Unit 1.31+ doesn’t support pre-0.8 njs versions; please update.
Some request properties are exposed as njs objects or scalars:
Name |
Type |
Description |
---|---|---|
|
Object |
Query string arguments;
|
|
Object |
Request cookies;
an |
|
Object |
Request header fields;
|
|
Scalar |
|
|
Scalar |
Remote IP address of the request. |
|
Scalar |
Request target, percent decoded and normalized by removing the query string and resolving relative references (“.” and “..”, “//”). |
Template lterals 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
Mind that the js_module
option
can be a string or an array,
so 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 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`"
}
For further reference, see the njs documentation.