nginx njs: установка модуля, настройка js_set и применение через reload для создания переменных JavaScript

nginx njs — официальный модуль nginx, который обрабатывает запросы или создаёт значения переменных функциями JavaScript прямо в конфигурации nginx. На сервере с конфигурацией Nginx + Apache, чтобы выровнять регистр URL запросов, чего нельзя сделать одной конфигурацией nginx, модуль njs был установлен в той же версии, что и установленный nginx, а переменная, созданная через js_set, подставлена в путь к файлу кэша и применена через reload без перезапуска.
Функция из примера — одна строка, переводящая процентное кодирование корейских URL в нижний регистр; переменные из значений заголовков или аргументов объекта запроса создаются так же.
1️⃣ Что такое nginx njs: модуль, расширяющий nginx с помощью JavaScript
njs — не самостоятельная среда выполнения JavaScript, как Node.js: у него нет стартового скрипта и собственного цикла событий, код выполняется, только когда nginx вызывает указанную в конфигурации функцию, после чего обработка возвращается к nginx.
Точки, где nginx вызывает функции JavaScript, определены заранее: этапы обработки HTTP-запросов и ответов, этапы обработки сессий TCP·UDP, вычисление значений переменных и периодические таймеры. Функция читает URL, заголовки и аргументы из объекта запроса, который передаёт nginx, и возвращает результат как ответ или значение переменной.
Со стороны HTTP [js_import] загружает файл скрипта, [js_set] создаёт значение переменной, [js_content] — тело ответа, а [js_header_filter] и [js_body_filter] изменяют заголовки и тело ответа. В этой настройке использованы две директивы: [js_import] и [js_set].
Стандартный движок модуля njs реализует ECMAScript 5.1 (strict mode) с частью возможностей ECMAScript 6 и новее. Обработка строк на уровне замены по регулярным выражениям и функций работает в этих пределах.
2️⃣ Когда нужен njs: значения переменных, которые не изменить одной конфигурацией nginx
Плагин WordPress WP Rocket называет папки кэша записей с корейскими URL в процентном кодировании нижнего регистра, а браузеры запрашивают тот же URL, закодированный в верхнем регистре. На этом сервере работает сайт WordPress.
Если nginx строит путь к файлу кэша по URL запроса как есть, регистр не совпадает, файл не находится, и запрос уходит в Apache и PHP.
Одной конфигурацией nginx это значение в нижний регистр не перевести. [map] лишь выбирает одно из заданных значений и не преобразует строки, а до установки ни в выводе [nginx -V], ни в папке модулей не было модулей perl или lua.
Если менять нужно лишь несколько URL, справится и таблица соответствий [map], но её придётся пересобирать и делать reload при каждой новой записи. Когда каждый URL нужно преобразовать по одному правилу, одна функция [js_set] охватывает и URL новых записей, меток и рубрик.

[perl_set] из модуля perl тоже можно использовать в той же точке, и в репозитории есть парный nginx пакет, но официальная документация nginx называет модуль perl экспериментальным.
3️⃣ Установка nginx njs: в той же версии, что и установленный nginx
Установка только по имени пакета модуля выглядит так, будто добавится лишь njs, но последний njs в репозитории требует последний nginx, поэтому nginx тоже обновится. При проверке перед установкой команда установки без версии показала план обновления nginx с 1.28.0 до 1.30.6.
Поэтому сначала проверьте версию установленного nginx.
rpm -q nginxnginx-1.28.0-1.el9.ngx.x86_64В репозитории для каждой версии nginx есть свой парный пакет njs; в номере версии часть до + — версия nginx, а часть после — версия njs.
dnf list --showduplicates nginx-module-njs | grep '1.28.0+'nginx-module-njs.x86_64 1:1.28.0+0.9.4-1.el9.ngx @nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.8.10-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.0-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.1-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.2-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.3-1.el9.ngx nginx-stable
nginx-module-njs.x86_64 1:1.28.0+0.9.4-1.el9.ngx nginx-stableС nginx 1.28.0 в паре шесть версий — от 0.8.10 до 0.9.4, а верхняя строка @nginx-stable — уже установленная версия. Устанавливается самая новая из них, 0.9.4, с полным номером версии.
dnf install nginx-module-njs-1:1.28.0+0.9.4-1.el9.ngxКакую версию nginx требует установленный пакет, можно проверить через rpm.
rpm -q --requires nginx-module-njs | grep nginxnginx-r1.28.0rpm -ql nginx-module-njs | grep -v build-id/usr/bin/njs
/usr/lib64/nginx/modules/ngx_http_js_module-debug.so
/usr/lib64/nginx/modules/ngx_http_js_module.so
/usr/lib64/nginx/modules/ngx_stream_js_module-debug.so
/usr/lib64/nginx/modules/ngx_stream_js_module.so
/usr/share/doc/nginx-module-njs
/usr/share/doc/nginx-module-njs/CHANGES
/usr/share/doc/nginx-module-njs/COPYRIGHTУстановлены HTTP-модуль, модуль stream для TCP·UDP, отладочная версия каждого из них и инструмент командной строки njs. В этой настройке загружается только один файл — HTTP-модуль [ngx_http_js_module.so].
4️⃣ Настройка js_set: от загрузки модуля до пути к файлу кэша
Установка пакета не заставляет nginx загружать модуль автоматически: в самом верху nginx.conf нужна строка [load_module]. Согласно официальной документации, [load_module] — директива контекста main, поэтому она размещается вне блоков events·http.
# самый верх /etc/nginx/nginx.conf (вне блоков events·http)
load_module modules/ngx_http_js_module.so;Функция преобразования хранится в отдельном от конфигурации файле .js, а конфигурация nginx загружает функцию, экспортированную через export default в последней строке.
// /etc/nginx/njs/rocket.js — в нижний регистр только процентное кодирование %XX
function lc(r) {
return r.variables.request_uri.replace(/%[0-9A-F]{2}/g, function (m) { return m.toLowerCase(); });
}
export default { lc };Функция читает переменную [request_uri] из объекта запроса r, переводит в нижний регистр только два символа после знака процента, если они заглавные, и возвращает результат. Латинские буквы не трогаются, поэтому английские URL с заглавными буквами не меняются; это совпадает с правилом, по которому WP Rocket называет папки кэша.
# внутри блока http (файл .conf в conf.d)
js_import rkt from /etc/nginx/njs/rocket.js;
js_set $rocket_uri rkt.lc;rkt в [js_import] — имя для вызова функций внутри модуля, а [js_set] превращает результат этой функции в переменную [$rocket_uri]. Согласно официальной документации, функция выполняется при первом обращении к переменной в запросе и поддерживает только синхронную обработку.
# блок server — $request_uri в пути к файлу кэша заменяется на $rocket_uri
set $rocket_file '/wp-content/cache/wp-rocket/example.com${rocket_uri}index$rocket_mobile-https.html';Изменена только эта одна строка, строящая путь к файлу кэша; условия, исключающие из кэша cookie входа, запросы с параметрами и POST-запросы, остаются прежними.
5️⃣ Применение и проверка: журнал ошибок и заголовки ответа после reload
Сначала выполните проверку конфигурации, а если она пройдёт — reload.
nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfulsystemctl reload nginxНовая строка [load_module] применилась через reload без перезапуска. В журнале ошибок осталась строка об инициализации виртуальной машины njs, ошибок JavaScript не было.
grep 'js vm init' /var/log/nginx/error.log | head -12026/10/17 18:21:35 [notice] 27992#27992: js vm init njs: 0000559FDF6D0E00njs в конце строки — имя используемого движка. Та же строка пишется и при проверке конфигурации через [nginx -t], поэтому сверяйте её со временем reload.
curl -sI 'https://example.com/-/' | grep -i 'x-rocket\|content-type'content-type: text/html
x-rocket-nginx-serving-file: HITДаже для корейского URL, закодированного в верхнем регистре, появился заголовок, показывающий, что nginx отдал файл кэша. Запросы с параметрами или cookie входа и POST-запросы после изменения по-прежнему уходили в PHP без этого заголовка.
6️⃣ Откат njs и обновление nginx
Для отката замените [$rocket_uri] в пути к файлу кэша обратно на [$request_uri], уберите файл конфигурации [js_import]·[js_set] и строку [load_module], затем выполните проверку конфигурации и reload.
# блок server — обратно к исходной переменной
set $rocket_file '/wp-content/cache/wp-rocket/example.com${request_uri}index$rocket_mobile-https.html';Пакет можно оставить: без строки [load_module] nginx его не загружает, поэтому удаляйте пакет, только если хотите убрать его полностью.
dnf remove nginx-module-njsПри обновлении nginx пакет njs тоже должен обновиться до версии, парной новому nginx. Перед обновлением проверьте через [dnf list --showduplicates], есть ли в репозитории пара njs для новой версии nginx.
🔢 FAQ & рекомендуемые материалы














ℹ️ О партнёрских ссылках
Материалы этого сайта содержат партнёрские ссылки. Если посетитель покупает товар или услугу по такой ссылке, сайт получает комиссию от продавца. При этом сумма, которую платит покупатель (при акционных скидках сумма снижается ↓), не увеличивается. Указанные цены, скидки и наличие актуальны на момент написания и могут отличаться, поэтому перед покупкой уточняйте их у продавца. Товары выбираются и оцениваются по нашим собственным критериям, а комиссия не влияет ни на порядок, ни на содержание обзоров.

