Welcome to the latest installment in our series of blogs about new features in NGINX Unit! Admittedly, it’s been a while since we provided an update, so the time has come to share the details about our latest versions – NGINX Unit 1.23.0 and 1.24.0 – and explore how they make the life of an NGINX Unit enthusiast a whole lot easier.
But first, some great news about our increasingly international team: two more developers, Oisín Canty and Zhidao Hong (洪志道), joined us this year, bringing with them lots of diverse expertise, some serious affection for NGINX Unit, and a major boost in overall productivity. Both have hit the ground running, coming up with a plethora of new features in mere months and participating in the design and development of the capabilities discussed below. Meanwhile, Andrei Suvorov, our next‑newest team member, has already proven his worth, providing essential developments to our latest releases. Speaking of which…
TLS: SNI and Configuration Commands
The first set of changes to NGINX Unit revolves around its implementation of the TLS protocol. Version 1.23.0 introduces support for the Server Name Indication (SNI) extension to TLS, providing a straightforward way to map certificates between the multiple sites and domains you serve from a single NGINX Unit deployment.
First, let’s briefly review how NGINX Unit has handled certificates since support for TLS was added in version 1.4.0: you bundle up certificate chains, upload them via the control API, and assign the bundles to listeners. Pretty neat, but the one-to-one mapping between bundles and listeners had its limitations. Now you can specify an array of bundles in a single listener:
Behind the scenes, NGINX Unit works its magic to make the most appropriate choice for each arriving request, using the common and alternative names of the certificates in each bundle. If the client specifies a server name, NGINX Unit responds with a certificate from the corresponding bundle. If the name matches multiple bundles, exact matches have priority over wildcards such as *.example.com. If any ambiguity remains, NGINX Unit uses the first bundle on the list, which it also does when there’s no match or the request doesn’t include a server name at all.
Finally, those familiar with the NGINX
ssl_ciphers directives will probably rejoice that NGINX Unit now also enables you to supply a set of OpenSSL configuration commands per listener:
Static Content: MIME Filtering
Unit’s content‑handling abilities received a major boost in version 1.24.0. You can now use Unit’s support of built‑in and customizable MIME types in your routing schemes. Simple as that may seem, it tidies up routing schemes that involve static content sharing. Consider this
Here, we effectively separate static content from .php scripts within a single
share action, achieving what previously required two different route steps.
types array supports the same pattern mechanism that the route conditions use, so we can employ some neat magic to restrict the static content types we serve. When NGINX Unit cannot infer the MIME type of a requested file, it considers the type to be empty. Thus, we can configure Unit to serve only files whose MIME types it recognizes by including the negated empty string (
"!") – meaning “do not serve files with empty MIME types” – in the
Note that you can also define custom MIME types to control which file types Unit serves. In this example, we define the set of filenames and extensions with type
text/plain that Unit recognizes and allows to be used in the
This shows how you can use the global
mime_types option to extend the capabilities of the
types option beyond what’s built in, adapting it to your routing purposes.
Static Content: Chrooting and Path Restrictions
NGINX Unit 1.24.0 introduces three new options for fine‑tuning how Unit serves static content on servers running Linux kernel version 5.6 and higher. They are intended to prevent accidental or deliberate misuse of NGINX Unit’s mechanics for serving static content. The first one is
This option sets the root directory for pathname resolution within the directory named by the
share option. One notable effect is that symbolic links to absolute pathnames within the
share directory are treated as relative to the new root. Given the configuration above, for example, if you create a symbolic link in /www/data/static/ to /log/app.log, it is resolved as /www/data/log/app.log. Note, however, that the treatment of symbolic links is also affected by the setting of the
follow_symlinks option, discussed next.
The two remaining options,
traverse_mounts, control NGINX Unit’s attitude toward (surprise!) symbolic links and mount points:
When these options are set to
false (the default is
true), requests fail if they require resolution of a symbolic link or mount point within the
share directory (here, /www/data/static/); Unit is effectively locked within the confines of the
The interaction of
traverse_mounts can be bit intricate so let’s take a moment to consider the merged configuration:
chroot is set, the values of
traverse_mounts only affect portions of the path after the new root. This means that subdirectories that are a part of the chroot path (here, www/ and data/) can be symbolic links or mount points, but any symbolic links or mount points beyond the final element in the
chroot path (here, that includes static/) are not resolved.
We hope you’ll be glad to learn that Node.js® apps now require zero code alterations to run in Unit. This is achieved with a new loader module (unit-http/loader.mjs) that works behind the scenes at application startup, laying all the necessary groundwork for a truly server‑agnostic app:
Admittedly, this looks a bit cumbersome. It looks like a win to us, though, because it makes a significant redesign unnecessary, instead achieving the desired effect by using NGINX Unit’s existing mechanics for running a designated executable (here, /usr/bin/env).
NGINX Unit 1.24.0 extends to Python apps the level of granularity previously granted only to PHP apps. You can configure several scripts within a single app to simplify your routing and application setup:
This example puts two modules,
bar/wsgi.py, into the context of a single app and its processes, which conserves system resources otherwise consumed by running multiple scripts that comprise a single app. Elsewhere in the configuration, you can pass requests to these portions of your app quite independently:
In this blog post, we’ve discussed Unit versions 1.23.0 and 1.24.0, but version 1.25.0 is not too far away, so stay alert for the new features and numerous bug fixes it’s about to bring to your table.
As always, we invite you to check out our roadmap, where you can find out whether your favorite feature is going to be implemented any time soon, and rate and comment on our in‑house initiatives. Feel free to open new issues in our repo on GitHub and share your ideas for improvement.
For a complete list of the changes and bug fixes in versions 1.23.0 and 1.24.0, see the NGINX Unit changelog.