1. Совпадения по регулярным выражениям основаны на префиксе и нечувствительны к регистру
Как было объявлено в ноябре 2025 года, Kubernetes прекратит поддержку Ingress-NGINX в марте 2026 года. Несмотря на широкое распространение, Ingress-NGINX полон неочевидных настроек по умолчанию и побочных эффектов, которые с высокой вероятностью присутствуют и в вашем кластере. Эта статья описывает подобные поведения, чтобы вы могли безопасно провести миграцию и осознанно решить, какие из них стоит сохранить. Здесь также сравниваются Ingress-NGINX и Gateway API и показано, как воспроизвести поведение Ingress-NGINX средствами Gateway API. Сквозной риск во всех разделах один и тот же: внешне корректный перенос конфигурации всё равно может привести к сбоям, если не учесть особенности Ingress-NGINX.
Предполагается, что читатель уже знаком с Ingress-NGINX и Ingress API. В большинстве примеров в качестве бэкенда используется httpbin.
Обратите также внимание, что Ingress-NGINX и NGINX Ingress — это два разных Ingress-контроллера. Ingress-NGINX — контроллер, поддерживаемый сообществом Kubernetes, который прекращает работу в марте 2026 года. NGINX Ingress — контроллер от компании F5. Оба используют NGINX в качестве плоскости данных (dataplane), но в остальном никак не связаны. Далее в статье речь идёт исключительно об Ingress-NGINX.
1. Совпадения по регулярным выражениям основаны на префиксе и нечувствительны к регистру
Предположим, вы хотите направлять все запросы, путь которых состоит ровно из трёх заглавных букв, к сервису httpbin. Для этого можно создать следующий Ingress с аннотацией nginx.ingress.kubernetes.io/use-regex: "true" и шаблоном регулярного выражения /[A-Z]{3}.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/[A-Z]{3}"
pathType: ImplementationSpecific
backend:
service:
name: httpbin
port:
number: 8000
Однако поскольку совпадения по регулярным выражениям работают как нечувствительные к регистру префиксные совпадения, Ingress-NGINX направит к httpbin любой запрос, путь которого начинается с любых трёх букв:
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/uuid
Вывод будет примерно таким:
{
"uuid": "e55ef929-25a0-49e9-9175-1b6e87f40af7"
}
Примечание: Эндпоинт /uuid в httpbin возвращает случайный UUID. Наличие UUID в теле ответа означает, что запрос успешно дошёл до httpbin.
В Gateway API для сопоставления пути по регулярному выражению можно воспользоваться HTTP path match с типом RegularExpression. Совпадения типа RegularExpression определяются реализацией, поэтому уточните у своей реализации Gateway API, какова семантика такого сопоставления. Популярные реализации Gateway API на базе Envoy — например, Istio, Envoy Gateway и Kgateway — выполняют полное совпадение с учётом регистра.
Таким образом, если вы не знали, что шаблоны Ingress-NGINX работают как нечувствительные к регистру префиксы, и при этом клиенты или приложения отправляют запросы на /uuid (или /uuid/some/other/path), вы можете создать такой HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Измените в соответствии с вашим сценарием
rules:
- matches:
- path:
type: RegularExpression
value: "/[A-Z]{3}"
backendRefs:
- name: httpbin
port: 8000
Однако если ваша реализация Gateway API выполняет полное совпадение с учётом регистра, этот HTTPRoute не будет соответствовать запросу с путём /uuid. Это приведёт к сбою: запросы, которые Ingress-NGINX ранее направлял в httpbin, получат ответ 404 Not Found на шлюзе (gateway).
Чтобы сохранить нечувствительное к регистру совпадение по регулярному выражению, используйте следующий HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
parentRefs:
- name: <your gateway> # Измените в соответствии с вашим сценарием
rules:
- matches:
- path:
type: RegularExpression
value: "/[a-zA-Z]{3}.*"
backendRefs:
- name: httpbin
port: 8000
Как вариант, упомянутые прокси поддерживают флаг (?i) для указания нечувствительности к регистру. С его помощью шаблон будет выглядеть как (?i)/[a-z]{3}.*.
2. Аннотация nginx.ingress.kubernetes.io/use-regex распространяется на все пути хоста во всех Ingress-ресурсах (Ingress-NGINX)
Допустим, у вас есть Ingress с аннотацией nginx.ingress.kubernetes.io/use-regex: "true", но вы хотите направлять запросы с точным путём /headers к httpbin. При этом вы допустили опечатку и указали путь /Header вместо /headers.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "<some regex pattern>"
pathType: ImplementationSpecific
backend:
service:
name: <your backend>
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: regex-match-ingress-other
spec:
ingressClassName: nginx
rules:
- host: regex-match.example.com
http:
paths:
- path: "/Header" # опечатка: должно быть /headers
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Большинство ожидало бы, что запрос к /headers получит ответ 404 Not Found, так как /headers не совпадает точно с путём /Header при типе Exact. Однако поскольку Ingress regex-match-ingress содержит аннотацию nginx.ingress.kubernetes.io/use-regex: "true" и хост regex-match.example.com, все пути с хостом regex-match.example.com трактуются как регулярные выражения — во всех Ingress-ресурсах (Ingress-NGINX). Так как шаблоны регулярных выражений — это нечувствительные к регистру префиксные совпадения, /headers соответствует шаблону /Header, и Ingress-NGINX направляет такие запросы к httpbin. Выполнив команду:
curl -sS -H "Host: regex-match.example.com" http://<your-ingress-ip>/headers
вы получите вывод вида:
{
"headers": {
...
}
}
Примечание: Эндпоинт /headers в httpbin возвращает заголовки запроса. Наличие заголовков запроса в теле ответа означает, что запрос успешно дошёл до httpbin.
Gateway API не преобразует молчаливо совпадения типа Exact и Prefix в шаблоны регулярных выражений. Поэтому если вы перенесёте приведённые Ingress-ресурсы в следующий HTTPRoute, сохранив опечатку и типы совпадений, запросы к /headers будут получать ответ 404 Not Found вместо 200 OK.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: regex-match-route
spec:
hostnames:
- regex-match.example.com
rules:
...
- matches:
- path:
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
Чтобы сохранить нечувствительное к регистру префиксное совпадение, замените:
- matches:
- path:
type: Exact
value: "/Header"
на:
- matches:
- path:
type: RegularExpression
value: "(?i)/Header"
Или, что ещё лучше, исправьте опечатку и укажите:
- matches:
- path:
type: Exact
value: "/headers"
3. Аннотация rewrite-target неявно включает режим регулярных выражений
Предположим, вы хотите переписать путь запросов с /ip на /uuid перед отправкой в httpbin и, как в разделе 2, направлять запросы с точным путём /headers к httpbin. Однако вы случайно допускаете опечатки: указываете /IP вместо /ip и /Header вместо /headers.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/uuid"
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/IP"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rewrite-target-ingress-other
spec:
ingressClassName: nginx
rules:
- host: rewrite-target.example.com
http:
paths:
- path: "/Header"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Аннотация nginx.ingress.kubernetes.io/rewrite-target: "/uuid" приводит к тому, что у запросов, соответствующих путям в Ingress rewrite-target-ingress, путь перезаписывается на /uuid перед маршрутизацией к бэкенду.
Несмотря на то что ни один из Ingress-ресурсов не содержит аннотацию nginx.ingress.kubernetes.io/use-regex: "true", наличие аннотации nginx.ingress.kubernetes.io/rewrite-target в Ingress rewrite-target-ingress приводит к тому, что все пути с хостом rewrite-target.example.com трактуются как шаблоны регулярных выражений. Иными словами, nginx.ingress.kubernetes.io/rewrite-target молчаливо добавляет аннотацию nginx.ingress.kubernetes.io/use-regex: "true" со всеми описанными выше побочными эффектами. Например, запрос к /ip получит перезаписанный путь /uuid, так как /ip соответствует нечувствительному к регистру префиксному шаблону /IP в Ingress rewrite-target-ingress. После выполнения команды:
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/ip
вывод будет примерно таким:
{
"uuid": "12a0def9-1adg-2943-adcd-1234aadfgc67"
}
Аналогично примеру с nginx.ingress.kubernetes.io/use-regex, Ingress-NGINX трактует пути других Ingress-ресурсов с хостом rewrite-target.example.com как нечувствительные к регистру префиксные шаблоны. Выполнив команду:
curl -sS -H "Host: rewrite-target.example.com" http://<your-ingress-ip>/headers
вы получите вывод вида:
{
"headers": {
...
}
}
Перезапись путей в Gateway API настраивается с помощью фильтра HTTP URL rewrite, который не преобразует молчаливо совпадения типа Exact и Prefix в шаблоны регулярных выражений. Однако если вы не знаете о побочных эффектах аннотации nginx.ingress.kubernetes.io/rewrite-target и не осознаёте, что /Header и /IP — опечатки, вы можете создать следующий HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: rewrite-target-route
spec:
hostnames:
- rewrite-target.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/IP"
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplaceFullPath
replaceFullPath: /uuid
backendRefs:
- name: httpbin
port: 8000
- matches:
- path:
# Это точное совпадение, независимо от других правил
type: Exact
value: "/Header"
backendRefs:
- name: httpbin
port: 8000
Как и в разделе 2, поскольку /IP теперь является совпадением типа Exact в вашем HTTPRoute, запросы к /ip будут получать ответ 404 Not Found вместо 200 OK. Аналогично запросы к /headers тоже будут получать 404 Not Found вместо 200 OK. Таким образом, этот HTTPRoute нарушит работу приложений и клиентов, использующих маршруты /ip и /headers.
Чтобы исправить это, можно заменить типы совпадений в HTTPRoute на регулярные выражения и сделать шаблоны нечувствительными к регистру префиксными совпадениями:
- matches:
- path:
type: RegularExpression
value: "(?i)/IP.*"
...
- matches:
- path:
type: RegularExpression
value: "(?i)/Header.*"
Или сохранить тип совпадения Exact и исправить опечатки.
4. Запросы без завершающего слэша перенаправляются на тот же путь с завершающим слэшем
Рассмотрим следующий Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: trailing-slash-ingress
spec:
ingressClassName: nginx
rules:
- host: trailing-slash.example.com
http:
paths:
- path: "/my-path/"
pathType: Exact
backend:
service:
name: <your-backend>
port:
number: 8000
Можно было бы ожидать, что Ingress-NGINX ответит на запрос к /my-path кодом 404 Not Found, так как /my-path не совпадает точно с путём /my-path/ при типе Exact. Однако Ingress-NGINX перенаправляет запрос на /my-path/ с кодом 301 Moved Permanently, поскольку единственное различие между /my-path и /my-path/ — завершающий слэш.
curl -isS -H "Host: trailing-slash.example.com" http://<your-ingress-ip>/my-path
Вывод будет выглядеть так:
HTTP/1.1 301 Moved Permanently
...
Location: http://trailing-slash.example.com/my-path/
...
То же самое происходит при изменении pathType на Prefix. Однако редирект не срабатывает, если путь задан шаблоном регулярного выражения.
Соответствующие стандарту реализации Gateway API не настраивают никаких редиректов молчаливо. Если клиенты или сервисы рассчитывают на этот редирект, миграция на Gateway API без явной настройки перенаправления запросов приведёт к сбою: запросы к /my-path будут получать ответ 404 Not Found вместо 301 Moved Permanently. Редирект можно настроить явно с помощью фильтра HTTP request redirect:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: trailing-slash-route
spec:
hostnames:
- trailing-slash.example.com
parentRefs:
- name: <your-gateway>
rules:
- matches:
- path:
type: Exact
value: "/my-path"
filters:
requestRedirect:
statusCode: 301
path:
type: ReplaceFullPath
replaceFullPath: /my-path/
- matches:
- path:
type: Exact # или Prefix
value: "/my-path/"
backendRefs:
- name: <your-backend>
port: 8000
5. Ingress-NGINX нормализует URL
Нормализация URL (URL normalization) — это процесс приведения URL к канонической форме перед сопоставлением с правилами Ingress и маршрутизацией. Детали нормализации URL описаны в RFC 3986, раздел 6.2; примеры преобразований:
-
удаление сегментов пути, равных
.:my/./path → my/path -
удаление предыдущего сегмента при наличии
..:my/../path → /path -
устранение дублирующихся слэшей в пути:
my//path → my/path
Ingress-NGINX нормализует URL перед сопоставлением с правилами Ingress. Рассмотрим следующий Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-normalization-ingress
spec:
ingressClassName: nginx
rules:
- host: path-normalization.example.com
http:
paths:
- path: "/uuid"
pathType: Exact
backend:
service:
name: httpbin
port:
number: 8000
Ingress-NGINX нормализует путь следующих запросов до /uuid. После этого запрос соответствует пути /uuid с типом Exact, и Ingress-NGINX возвращает либо ответ 200 OK, либо перенаправление 301 Moved Permanently на /uuid.
Для следующих команд:
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/uuid
curl -sS -H "Host: path-normalization.example.com" http://<your-ingress-ip>/ip/abc/../../uuid
curl -sSi -H "Host: path-normalization.example.com" http://<your-ingress-ip>////uuid
выводы будут примерно такими:
{
"uuid": "29c77dfe-73ec-4449-b70a-ef328ea9dbce"
}
{
"uuid": "d20d92e8-af57-4014-80ba-cf21c0c4ffae"
}
HTTP/1.1 301 Moved Permanently
...
Location: /uuid
...
Ваши бэкенды могут рассчитывать на нормализацию URL на стороне Ingress или реализации Gateway API. При этом большинство реализаций Gateway API по умолчанию выполняют некоторую нормализацию путей. Например, Istio, Envoy Gateway и Kgateway нормализуют сегменты . и .. из коробки. За подробностями обращайтесь к документации используемой вами реализации Gateway API.
Заключение
Пока мы все спешим отреагировать на завершение поддержки Ingress-NGINX, я надеюсь, что эта статья поможет вам почувствовать уверенность в том, что миграция может быть проведена безопасно и эффективно — несмотря на все тонкости Ingress-NGINX.
SIG Network также работает над поддержкой наиболее распространённых аннотаций Ingress-NGINX (и некоторых из этих неожиданных поведений) в Ingress2Gateway, чтобы помочь перевести конфигурацию Ingress-NGINX в Gateway API и предложить альтернативы для неподдерживаемых возможностей.
SIG Network выпустила Gateway API 1.5 сегодня (27 февраля 2026 года), выведя в статус стабильных такие функции, как ListenerSet (позволяет разработчикам приложений удобнее управлять TLS-сертификатами) и фильтр CORS для HTTPRoute, обеспечивающий настройку CORS.
Istio можно использовать исключительно как контроллер Gateway API, без прочих функций сервисной сетки (service mesh).