Примечание 1: при написании оригинала этого поста искусственный интеллект не пострадал.
Примечание 2: этот перевод блог-поста с blog.nginx.com выполнен ИИ, потом вычитан и отредактирован лично автором.
Введение
В сентябре 2026 года мы выпустили NGINX 1.31.5 с несколькими ключевыми фичами, объединёнными одной целью. Мы расширили базовые методы маршрутизации и самые важные директивы nginx, чтобы обеспечить прямую, не требующую скриптов маршрутизацию любого API-трафика. В этом посте мы разберём самую важную возможность, которую мы назвали «предикатные локейшены» (predicate locations), и объясним, как она сочетается с остальными улучшениями, которые мы делаем для поддержки современных приложений.
Проблема
В идеальном мире HTTP — отличный протокол. При правильном использовании он обеспечивает крайне быструю и надёжную коммуникацию, которая одновременно читаема человеком и программируема для клиентов и серверов.
HTTP-взаимодействие начинается со строки запроса вроде такой:
POST /api/v1/coffee HTTP/1.1
Метод (в примере — POST) предназначен для того, чтобы сообщить бэкенд-серверу, какое действие нужно выполнить.
URL (/api/v1/coffee) предназначен для указания на ресурс.
Заголовки, следующие за строкой запроса, предназначены для того, чтобы инструктировать сервер по различным деталям операций. Тело запроса содержит полезную нагрузку.
Маршрутизация HTTP-запросов обычно строится вокруг URL, а в некоторых случаях — вокруг методов.
Разработчики приложений игнорируют соглашения протокола и размещают модификаторы трафика внутри заголовков и тела запроса.
В результате веб-серверы, прокси, устройства безопасности и балансировщики нагрузки с трудом справляются с эффективной маршрутизацией трафика. Они не рассчитаны на то, что данные приложения окажутся не в тех местах.
Модернизация NGINX: предикатные локейшены
NGINX, как и любой другой промежуточный прокси, проектировался вокруг URL.
В NGINX 1.31.5 мы сняли это ограничение и позволили любой переменной становиться модификатором трафика для любого блока location в конфигурации.
Классические блоки location
Структура конфигурации NGINX в значительной степени построена на блоках location. В качестве иллюстративного (не буквального) примера посмотрите на такую структуру:
http {
server {
location / { ... }
location /images { ... }
location /api { ... }
location /api/v1/public { ... }
location /api/v1/configuration { ... }
location /api/v1/coffee { ... }
}
}
Такая структура позволяет размещать наиболее значимые и «рабочие» директивы в этих блоках location. Вы можете назначать разные пулы бэкенд-серверов (upstreams) для маршрутизации, настраивать различные уровни лимитов по скорости и количеству запросов, изменять заголовки в обоих направлениях, настраивать аутентификацию, WAF и другие средства безопасности — и всё это независимо для каждого location.
Мы не можем переопределить эту базовую структуру. Она слишком мощная, стабильная и гибко настраиваемая. Большинству пользователей NGINX она нравится.
Предикатные блоки location
С расширением locations до поддержки любых переменных типичная структура локейшенов становится более продвинутой. Например:
http {
server {
location / { ... }
location /images { ... }
location $post_requests { ... }
location $bot_traffic { ... }
location $internal_tests { ... }
location $ai_mcp_queries { ... }
}
}
NGINX вычисляет переменные. Когда переменная оказывается истинной (точнее, «чем угодно, кроме пустой строки или нуля»), вы попадаете в этот location.
Дальше делайте с этим трафиком всё, что хотите.
Маппинг предикатов
В NGINX есть хорошо известный модуль map. Отображение переменных в значения становится критически важным для предикатной логики.
Допустим, у вас есть список HTTP-методов, которые вы хотите считать «истинными» для использования в предикатном локейшене. Все остальные методы должны быть ложными. Используйте такой простой пример в вашей конфигурации:
map $request_method $restricted_methods {
POST 1;
PUT 1;
DELETE 1;
default 0;
}
Устанавливать значение по умолчанию в «0» не обязательно — по умолчанию всегда "", что вычисляется как false. Однако явное объявление может быть полезно при отладке.
В этом примере вы теперь можете использовать переменную $restricted_methods в других местах конфигурации, в том числе в предикатных локейшенах.
Вложенность предикатов (nesting)
Конфигурация, полная предикатов, может стать довольно громоздкой.
Если вычисление предиката сложное по своей природе — например, если он читает тело запроса, — производительность может упасть.
По этим причинам делайте структуру локейшенов такой, чтобы она соответствовала вашему приложению.
Предикатные локейшены поддерживают вложенность вместе с классическими локейшенами на основе URL. Мы не предписываем, как именно вы должны их вкладывать. У вашего приложения свои паттерны трафика, которые и подскажут разумный вариант.
В примере ниже мы сначала используем URI, а дальнейшее разделение трафика делаем уже по предикатам:
http {
server {
location /images { ... }
location /api/v1 {
location $restricted_requests {
location $post_requests { ... }
location $bot_traffic { ... }
}
location $internal_tests { ... }
location $ai_mcp_queries { ... }
}
}
}
Как альтернативный подход, вы можете сначала определить предикаты, а обычные локейшены разместить на следующем уровне:
http {
server {
location $public_methods { ... }
location $restricted_methods {
location /api/v1/private {
location $internal_ips { ... }
location $bot_traffic { ... }
}
location /api { ... }
location /wp-admin { ... }
}
}
}
Маршрутизация по телу HTTP-запроса и значениям из JSON
Предикаты изначально спроектированы так, чтобы работать с другими новыми возможностями NGINX — ранним чтением тела HTTP-запроса и встроенным разбором JSON.
JSON — стандартный формат для современных API. JSONPath — стандарт для поиска значений.
Теперь вы можете задавать переменные на основе отдельных полей и использовать их как предикаты в локейшенах.
Для примера давайте маршрутизировать трафик на основе единицы или нуля в поле «vip» вот в таком теле JSON:
{
"order_id": "1042",
"item": "latte",
"size": "medium",
"user": {
"name": "Nick",
"params": {
"vip": 1
}
}
}
Мы используем следующую конфигурацию NGINX:
events { }
http {
client_body_early_read on; # Необходимо включить для раннего чтения тела
json_set $vip $request_body user.params.vip;
server {
location $vip {
return 200 "VIP usern";
}
location / {
return 200 "Regular user, applying restrictionsn";
# limit_req ...;
}
}
}
Не забудьте включить раннее чтение тела запроса с помощью client_body_early_read.
Протестируем с помощью curl:
nick:~$ curl -X POST -d '{"order_id":"1042","item":"latte","size":"medium","user":{"name":"Somebody","params":{"vip":0}}}' 127.1:8085
Regular user, applying restrictions
nick:~$ curl -X POST -d '{"order_id":"1042","item":"latte","size":"medium","user":{"name":"Nick","params":{"vip":1}}}' 127.1:8085
VIP user
Эта конфигурация демонстрирует то, чего раньше не было: простоту. Мы использовали стандартные для индустрии инструменты, создали переменную и напрямую по ней маршрутизировали.
Разбор JSON и установка переменных не ограничены телом запроса. Вы можете взять любую переменную NGINX, например HTTP-заголовок или JWT, в качестве источника и распарсить её.
Что ещё можно сделать с этим примером маршрутизации? Отправить трафик на разные upstream-серверы, ограничить его по скорости, количеству запросов или IP-адресам. Изменить заголовки и полезную нагрузку. Применить ограничения на основе содержимого, включить или отключить application firewall. Весь набор возможностей NGINX теперь доступен через предельно простой интерфейс.
Сложная маршрутизация с NJS
NJS умеет выполнять множество функций. Он может полностью взять на себя слой маршрутизации NGINX. Однако если злоупотреблять NJS в конфигурации NGINX, она может стать громоздкой. Кроме того, до появления предикатов было сложно «выйти» из NJS обратно в локейшены NGINX. Нашим пользователям приходилось писать мегабайты кода на NJS только ради маршрутизации.
С предикатами NJS упрощается.
В следующем примере мы подсчитаем количество слов в теле HTTP-запроса, примем решение, не слишком ли длинный запрос, и по-разному направим трафик на основе этого решения. Мы используем директиву client_body_early_read, чтобы переменная $request_body создавалась на раннем этапе обработки, применим JavaScript для подсчёта слов и установим предикат $many_words. Затем используем его в директиве location.
JavaScript-файл http.js:
function counter(r) {
const words = r.variables['request_body'].trim().split(/s+/).length;
if (words > 10) {
return 1;
}
return 0;
}
export default {counter};
Конфигурационный файл NGINX nginx.conf:
events { }
http {
client_body_early_read on;
js_import /path/to/conf/http.js;
js_engine qjs;
js_set $many_tokens http.counter;
server {
location $many_tokens {
return 413 "Request body has too many tokensn";
}
location / {
return 200 "Request is small, OKn";
}
}
}
Тестирование с помощью curl:
nick:~$ curl -X POST -d "a b c d e f g h j k l" 127.1:80
Request body has too many tokens
nick:~$ curl -X POST -d "a b c d" 127.1:80
Request is small, OK
Как видите, мы избежали создания сложного набора редиректов и именованных локейшенов, штатно задействовали все возможности nginx через прямое использование директив location и применили всю мощь JavaScript для конкретной небольшой задачи.
Будущее: сложные условия
Мы работаем над добавлением встроенной поддержки сложной условной логики внутри NGINX. Цель — упростить обработку разнообразных сценариев трафика с точностью и эффективностью, сохраняя конфигурацию nginx простой и надёжной.
Вы сможете задавать сложные логические условия, очень похожие на текущую директиву «if», но без её оговорок и ограничений.
Эта возможность будет доступна в виде отдельной директивы, которая вычисляется в true/false в зависимости от условия. Вы сможете использовать её в предикатных локейшенах или в других местах, где нужна логика true/false.
Советы, приёмы и подводные камни
Если ваша конфигурация насыщена предикатами, используйте следующие рекомендации:
-
Создавайте catch-all локейшены. В стандартных конфигурациях обычный блок «location /» имеет смысл только на верхнем уровне вложенности. С предикатами он имеет смысл на любом уровне вложенности как fallback/catch-all. Размещайте его в конце блока.
-
Ограничивайте раннее чтение тела запроса только теми участками, где это действительно нужно. Большие тела запросов требуют много памяти и CPU для обработки. Директива
client_body_early_readподдерживает переменные. Используйте предикаты по HTTP-заголовкам или URL, чтобы включать её выборочно. Конечно, когда это возможно. -
Обращайте особое внимание на порядок предикатов в конфигурации. Из соображений производительности размещайте простые и часто срабатывающие предикатные локейшены вверху списка, а более сложные — ближе к низу.
-
Называйте предикаты осмысленно, чтобы позже вы могли эффективно их отслеживать и отлаживать.
-
Используйте специальные форматы логов для отслеживания переменных. Ваши логи могут показывать значения нескольких переменных в одной строке. Это поможет при отладке.
Заключение
Теперь NGINX позволяет маршрутизировать трафик практически по чему угодно. Новые фичи гармонично работают вместе:
-
предикатные локейшены;
-
раннее чтение тела запроса;
-
встроенный разбор JSON.
Все эти возможности — строительные блоки. Они бесшовно работают с остальной частью NGINX.
Мы не предписываем, как должно выглядеть ваше приложение, как организовать конфигурацию и как ею управлять. С помощью этих универсальных функций и примеров вы можете создать собственный прокси и балансировщик нагрузки, подходящий практически под любой паттерн трафика.
Смотрите справочную документацию на nginx.org:
-
NGINX Location Blocks: https://nginx.org/en/docs/http/ngx_http_core_module.html#location
-
NGINX JSON Module: https://nginx.org/en/docs/http/ngx_http_json_module.html
-
NGINX client_body_early_read Directive: https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_early_read
-
NGINX 1.31.5 Release Blog: https://blog.nginx.org/blog/nginx-1-31-5-control-api-predicate-locations-early-body-inspection-and-more
-
Change Log for NGINX: https://nginx.org/en/CHANGES
Автор: LEETE


