2 сентября 2026 года на nginx.org вышли сразу два релиза: основная ветка nginx 1.31.5 и модуль JavaScript njs 1.0.1. В 1.31.5 четыре заметных возможности: Control API, location по переменной (predicate locations), встроенный разбор JSON и директива client_body_early_read. В njs закрыты три уязвимости, одна из них с оценкой major.
Стабильная ветка по-прежнему 1.30.x. Последний стабильный пакет на момент этой новости: 1.30.4 от 15 июля 2026. В неё, как обычно, попадают только серьёзные ошибки и дыры. Следующая стабильная линейка 1.32 будет собрана уже из 1.31.x.
Кому это вообще касается
Типичный WordPress за nginx с PHP-FPM эти новинки сами по себе не включают. Location по JSON-телу, Control API и раннее чтение POST нужны там, где nginx режет API, GraphQL, MCP или свой шлюз. Для обычного сайта с парой location и fastcgi_pass это скорее обзор, чем обязательный апгрейд.
Есть два исключения. Первое: если nginx проксирует HTTP/2 с буферизацией ответа, в 1.31.5 закрыт use-after-free в worker. В официальном блоге это прямо названо основной причиной обновиться, даже без новых директив. Второе: если в конфиге есть js_import и njs (особенно js_access, ngx.fetch() или XML Canonicalization), смотреть нужно на njs 1.0.1, а не только на номер nginx.
Что нового в nginx 1.31.5
Полный список в CHANGES. Разбор с примерами: запись в блоге nginx от 2 сентября 2026.
Location по переменной
Раньше location выбирался по URI: префикс, regexp, именованный блок. Теперь в определении location можно поставить переменную. Если она не пустая и не равна 0, блок считается совпавшим. Это тот же предикат, что у early_hints и access_log if=, только теперь им выбирают сам location.
Пример из разбора OpenNET (переменная из map по аргументу запроса):
map $arg_foo $pred {
bar 1;
qux 1;
}
location $pred {
root html;
}
На практике это убирает пачку if в одном location. Условие можно собрать из заголовка, подсети, результата map или поля JSON, если тело уже прочитано. Скорость та же C-логика, без njs и Lua.
Раннее чтение тела: client_body_early_read
Обычный порядок: заголовки, выбор location, потом тело. Директива client_body_early_read (контекст http и server) читает тело сразу после заголовков, настройками из server. Если хотя бы один параметр не пустой и не 0, чтение начинается до выбора location.
map $http_content_type $is_json {
application/json 1;
}
server {
listen 8000;
client_body_early_read $is_json;
client_max_body_size 256;
client_body_buffer_size 256;
return 200 $request_body;
}
Ограничения из документации: несовместимо с модулями без буферизации тела (в том числе gRPC) и с теми, кто пишет тело в файл (DAV). client_body_in_file_only при раннем чтении игнорируется. Имеет смысл только если дальше по конфигу реально смотрят в $request_body или в поля JSON. Иначе вы просто раньше жрёте память и диск на каждый POST.
JSON в ядре: ngx_http_json_module
Отдельный модуль, без Lua и без njs. В блоге nginx приведён такой разбор поля method из тела:
json_set $request_body $json_method "method";
Вместе с client_body_early_read переменная заполняется до выбора location, и её уже можно поставить в map или в location $json_method. Это как раз сценарий «один URI /mcp или /graphql, а бэкенд разный в зависимости от метода внутри JSON».
Отдельной страницы модуля в дереве docs на момент материала ещё нет (запрос к /en/docs/http/ngx_http_json_module.html отдаёт 404). Синтаксис и смысл лучше сверять с блогом и исходниками PR #1642, а не с чужими шпаргалками под сторонний ngx_http_json_module из 2010-х.
Control API
REST-интерфейс в master-процессе: список воркеров, конфиг из памяти, перечитывание конфигурации с ответом в JSON, а не через «послал сигнал и пошёл в error.log». Возможность пришла из NGINX Plus (там она появилась в R37.0) и теперь есть в открытой сборке.
Запуск, как в официальном блоге: unix-сокет, не TCP-порт.
sudo nginx -l unix:/run/nginx-control.sock
Перечитать конфиг и сразу получить статус:
curl --unix-socket /run/nginx-control.sock -X PATCH http://localhost/1/control/config
В блоге прямо написано: API без аутентификации, это доступ к внутренностям процесса. Сокет только на файловой системе с правами для доверенных пользователей. Слушать порт в сети не стоит. В разборе OpenNET для сборки из исходников указан флаг --with-control-api. На готовом пакете это проверяется так:
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'control|configure'
Баги, из-за которых mainline имеет смысл даже без новых директив
Самый неприятный: при проксировании с буферизацией и ошибке на отдаче ответа HTTP/2-клиенту worker мог отдать клиенту уже освобождённую память или упасть. По HTTP/1.1 это маскировалось, по HTTP/2 флаг ошибки ставился на fake-соединение, и write-handler продолжал слать DATA-кадры в освобождённый heap. Фикс: PR #1664. Если у вас proxy_buffering on и HTTP/2 к клиенту, это не «косметика changelog».
Ещё из CHANGES и блога:
- worker мог не завершиться при исчерпании файловых дескрипторов, в логе появлялось
accept4() failed (9: Bad file descriptor); - запросы к FastCGI и uwsgi ломались, если имя параметра слишком длинное (128+ байт для FastCGI, 256+ для uwsgi): бэкенд закрывал соединение, nginx отвечал 502;
- отдельные правки HTTP/3 (CRYPTO-кадры в 1-RTT),
ngx_http_slice_moduleи memcached.
В самом nginx 1.31.5 новых CVE нет. Дыры этого дня относятся к njs.
njs 1.0.1: три уязвимости
njs: JavaScript внутри http и stream. С 1.0.0 (23 июня 2026) встроенный движок njs помечен как deprecated, рекомендуют QuickJS (js_engine qjs). 1.0.1 от 2 сентября: security и стабильность, без смены этой линии.
- CVE-2026-18329, medium. Обход контроля доступа в
js_access: если асинхронное продолжение чтения тела бросало исключение или unhandled rejection, nginx мог обработать запрос так, будто проверка прошла. Уязвимы 0.9.9–1.0.0, исправлено в 1.0.1.js_accessпоявился как раз в 0.9.9. - CVE-2026-78222, medium. Падение worker при чтении
Response.statusTextпосле ответа апстрима со статус-строкой без reason phrase. Уязвимы 0.5.1–1.0.0 (то есть почти все, кто пользуетсяngx.fetch()). - CVE-2026-78689, major. Переполнение кучи при разборе списка namespace prefix в
xml.exclusiveC14n(). Уязвимы 0.7.10–1.0.0.
Важная оговорка с той же страницы: если в nginx.conf нет js_import, nginx к JavaScript-уязвимостям njs не привязан. Код скриптов считается доверенным наравне с конфигом. Нет js_import, нет этих трёх CVE. Есть js_access на 0.9.9 или 1.0.0, обновление njs уже не «когда будет окно».
Кроме CVE в 1.0.1 починены reuse контекстов QuickJS, SharedDict.pop(), валидация Fetch Headers и r.headersOut, циклические ссылки Fetch/HTTP/Stream, плюс btoa()/atob() в QuickJS. Полный список: Changes with njs 1.0.1.
Что делать на рабочем сервере
Сначала версия, не «apt upgrade наугад».
nginx -v nginx -V 2>&1 | head -n 5
Если njs загружен динамически, номер модуля смотрите в пакете или в строке configure. В образах Docker официального nginx 1.31.5 переменные сборки уже NGINX_VERSION=1.31.5 и NJS_VERSION=1.0.1.
Рабочий сайт на пакете дистрибутива (Ubuntu, Debian) обычно отстаёт от mainline nginx.org. Прыгать с 1.18/1.24 из репозитория ОС сразу на 1.31.5 без стенда не стоит: меняется не только номер, но и набор модулей, пути и дефолты ветки 1.29–1.31 (keepalive к апстриму, HTTP/1.1 по умолчанию в proxy и так далее, это копилось не в одном релизе).
Если сознательно сидите на пакетах nginx.org mainline:
sudo apt update apt-cache policy nginx nginx-module-njs
Перед установкой: копия /etc/nginx, понимание, как откатиться. После замены бинарника:
sudo nginx -t
Только если тест проходит:
sudo systemctl reload nginx
Если в конфиге появился Control API, не выставляйте сокет в мир и не вешайте его на 80/443. Отдельный путь вроде /run/nginx-control.sock, владелец root, права 0600, доступ только с той же машины или через SSH.
Где обычно ломается
client_body_early_readплюс gRPC или DAV: документация прямо говорит «несовместимо».- Раннее чтение большого POST без лимита:
client_max_body_sizeиclient_body_buffer_sizeлучше задать явно, как в примере документации (там 256 байт, это учебный потолок, не норма для продакшена). - Control API на TCP: открытый неуполномоченный интерфейс к reload и содержимому конфига.
- Пакет njs из дистрибутива и nginx с nginx.org в разных эпохах: ABI модуля не обязан совпасть.
- Ожидание, что 1.31.5 «уже стабильный». Нет. Стабильный сейчас 1.30.4. 1.32 ещё впереди.
Как проверить, что вы на нужной версии
nginx -v systemctl is-active nginx sudo nginx -t
Для njs, если модуль в конфиге:
nginx -T 2>/dev/null | grep -E 'js_import|js_engine|load_module.*njs'
Если js_import есть, а пакет njs старше 1.0.1, для js_access и XML C14n это уже предмет обновления, а не «прочитаем changelog на выходных».
Коротко
nginx 1.31.5: mainline с нормальными новыми ручками (предикаты, JSON, раннее тело, Control API) и одним неприятным фиксом HTTP/2. njs 1.0.1: три CVE, из них major в XML. На обычном WordPress-VPS без njs и без HTTP/2-проксирования тела можно спокойно сидеть на 1.30.4. Если njs или HTTP/2 с буферизацией уже в бою, смотреть пакеты стоит сейчас, а новые директивы сначала на стенде.