Traefik Kubernetes Ingress NGINX Routing Configuration
Since v3.18
The Kubernetes Controller for Ingresses with NGINX annotations.
The Kubernetes NGINX Ingress Controller project has announced its retirement in March 2026 and will no longer receive updates or security patches. Traefik provides a migration path by supporting NGINX annotations, allowing you to transition your workloads without rewriting all your Ingress configurations. For more information about the NGINX Ingress Controller retirement, see the official Kubernetes blog announcement.
Ingress Discovery
The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster,
which may lead to duplicated routers if you are also using the Kubernetes Ingress provider.
We recommend to use IngressClass for the Ingresses you want to be handled by this provider,
or to use the watchNamespace or watchNamespaceSelector options to limit the discovery of Ingresses to a specific namespace or set of namespaces.
Overview
The NGINX Ingress Controller is officially projected to enter maintenance mode, leaving organizations with production workloads that need a practical migration path. This provider addresses that need by offering a secure bridge that respects existing configuration investments while enabling future innovation.
The Kubernetes Ingress NGINX provider watches for incoming ingresses events, such as the example below, and derives the corresponding Routing Configuration from it, which in turn will create the resulting routers, services, handlers, etc.
Annotations Support
This section lists all known NGINX Ingress annotations, split between those currently implemented (with limitations if any) and those not implemented. Limitations or behavioral differences are indicated where relevant.
Traefik Hub API Gateway does not expose all global configuration options to control default behaviors for ingresses.
Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-ingress as in NGINX.
Supported NGINX Annotations
The annotation support follows a data-driven approach based on real-world usage patterns. Rather than attempting to support every possible annotation immediately, the implementation focuses on the most commonly used annotations that appear in the majority of production deployments. This approach allows most organizations to migrate their core ingress configurations without modification while gradually adopting Traefik-native features for advanced use cases.
The provider architecture also positions teams for future transitions to the Gateway API standard, which represents the next evolution of Kubernetes networking. Starting with this provider means adopting a platform that actively leads Gateway API implementation while maintaining backward compatibility with existing ingress resources.
We welcome feedback on which unsupported annotations matter most for your deployments. The provider's architecture makes community contributions clear, and each addition expands the migration path for more use cases.
For a detailed discussion of the design philosophy and migration strategies, see the NGINX Ingress transition blog post.
Authentication
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/auth-type | |
nginx.ingress.kubernetes.io/auth-secret | |
nginx.ingress.kubernetes.io/auth-secret-type | |
nginx.ingress.kubernetes.io/auth-realm | |
nginx.ingress.kubernetes.io/auth-url | Traefik Hub API Gateway forwards the original request to the auth service instead of using an NGINX auth subrequest. NGINX auth cache and keepalive annotations are not supported. |
nginx.ingress.kubernetes.io/auth-signin | Redirects to signin URL on 401 response. |
nginx.ingress.kubernetes.io/auth-snippet | Requires auth-url and the provider allowSnippetAnnotations option. |
nginx.ingress.kubernetes.io/auth-method | Requires auth-url. Do not combine it with auth-snippet if the snippet already sets proxy_method. |
nginx.ingress.kubernetes.io/auth-response-headers |
SSL/TLS
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/ssl-redirect | Cannot opt-out per route if enabled globally. |
nginx.ingress.kubernetes.io/force-ssl-redirect | Cannot opt-out per route if enabled globally. |
nginx.ingress.kubernetes.io/ssl-passthrough | Some differences in SNI/default backend handling. |
nginx.ingress.kubernetes.io/proxy-ssl-server-name | |
nginx.ingress.kubernetes.io/proxy-ssl-name | |
nginx.ingress.kubernetes.io/proxy-ssl-verify | |
nginx.ingress.kubernetes.io/proxy-ssl-secret | |
nginx.ingress.kubernetes.io/auth-tls-secret | When validation fails, the rejection happens during the TLS handshake rather than returning a 400 Bad Request. |
nginx.ingress.kubernetes.io/auth-tls-verify-client | When validation fails, the rejection happens during the TLS handshake rather than returning a 400 Bad Request. |
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream | Requires auth-tls-secret. |
Session Affinity
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/affinity | |
nginx.ingress.kubernetes.io/affinity-mode | Only persistent mode is supported. Balanced and canary modes are not supported. |
nginx.ingress.kubernetes.io/session-cookie-name | |
nginx.ingress.kubernetes.io/session-cookie-secure | |
nginx.ingress.kubernetes.io/session-cookie-path | |
nginx.ingress.kubernetes.io/session-cookie-domain | |
nginx.ingress.kubernetes.io/session-cookie-samesite | |
nginx.ingress.kubernetes.io/session-cookie-max-age | |
nginx.ingress.kubernetes.io/session-cookie-expires |
Load Balancing & Backend
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/load-balance | Only round_robin is supported. Use upstream-hash-by for hash-based backend selection. |
nginx.ingress.kubernetes.io/backend-protocol | FCGI and AUTO_HTTP not supported. |
nginx.ingress.kubernetes.io/service-upstream | |
nginx.ingress.kubernetes.io/upstream-hash-by | Uses Traefik Hub API Gateway's HRW strategy with minimal NGINX variable interpolation. |
nginx.ingress.kubernetes.io/upstream-vhost | |
nginx.ingress.kubernetes.io/custom-headers | Adds custom headers to the response forwarded to the client. The list of allowed response headers is controlled by the provider option globalAllowedResponseHeaders, which must be configured for the annotation to take effect. |
nginx.ingress.kubernetes.io/default-backend | The fallback service must be in the same namespace as the Ingress. If the service exposes multiple ports, the first port receives the traffic. |
nginx.ingress.kubernetes.io/proxy-http-version | Only "1.1" is supported. "1.0" is ignored with a warning. |
nginx.ingress.kubernetes.io/canary | |
nginx.ingress.kubernetes.io/canary-by-header | |
nginx.ingress.kubernetes.io/canary-by-header-value | |
nginx.ingress.kubernetes.io/canary-by-header-pattern | |
nginx.ingress.kubernetes.io/canary-by-cookie | |
nginx.ingress.kubernetes.io/canary-weight | Values are clamped to the 0..canary-weight-total range. |
nginx.ingress.kubernetes.io/canary-weight-total | Minimum value is 100. |
CORS
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/enable-cors | Partial support. |
nginx.ingress.kubernetes.io/cors-allow-credentials | |
nginx.ingress.kubernetes.io/cors-allow-headers | |
nginx.ingress.kubernetes.io/cors-allow-methods | |
nginx.ingress.kubernetes.io/cors-allow-origin | |
nginx.ingress.kubernetes.io/cors-expose-headers | |
nginx.ingress.kubernetes.io/cors-max-age |
Routing
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/app-root | |
nginx.ingress.kubernetes.io/from-to-www-redirect | Does not support wildcard hosts. |
nginx.ingress.kubernetes.io/use-regex | |
nginx.ingress.kubernetes.io/rewrite-target | |
nginx.ingress.kubernetes.io/x-forwarded-prefix | Only applied together with rewrite-target. |
nginx.ingress.kubernetes.io/permanent-redirect | Defaults to a 301 Moved Permanently status code. |
nginx.ingress.kubernetes.io/permanent-redirect-code | Only valid 3XX HTTP status codes are accepted. |
nginx.ingress.kubernetes.io/temporal-redirect | Takes precedence over permanent-redirect. Defaults to a 302 Found status code. |
nginx.ingress.kubernetes.io/temporal-redirect-code | Only valid 3XX HTTP status codes are accepted. |
nginx.ingress.kubernetes.io/custom-http-errors | Uses the annotation or global default backend to serve intercepted errors. |
nginx.ingress.kubernetes.io/server-alias | Conflicts with an existing Ingress host rule are skipped. |
nginx.ingress.kubernetes.io/server-snippet | Requires the provider allowSnippetAnnotations option. |
nginx.ingress.kubernetes.io/configuration-snippet | Requires the provider allowSnippetAnnotations option. |
IP Allowlist / Whitelist
By default, the client IP is determined from the remote address of the incoming request.
When Traefik Hub API Gateway is behind a reverse proxy, the actual client IP is often found in the X-Forwarded-For header instead.
This can be configured globally using the provider option ipAllowListStrategy.
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/whitelist-source-range | |
nginx.ingress.kubernetes.io/allowlist-source-range |
Rate Limiting
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/limit-rps | Exceeding the limit returns 429 Too Many Requests instead of NGINX's default 503 Service Unavailable. |
nginx.ingress.kubernetes.io/limit-rpm | Exceeding the limit returns 429 Too Many Requests instead of NGINX's default 503 Service Unavailable. |
Buffering
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/proxy-request-buffering | |
nginx.ingress.kubernetes.io/proxy-body-size | |
nginx.ingress.kubernetes.io/client-body-buffer-size | |
nginx.ingress.kubernetes.io/proxy-buffering | |
nginx.ingress.kubernetes.io/proxy-buffer-size | |
nginx.ingress.kubernetes.io/proxy-buffers-number | Used with proxy-buffer-size to compute the total in-memory response buffer size. |
nginx.ingress.kubernetes.io/proxy-max-temp-file-size |
Timeout
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/proxy-connect-timeout | Can also be set globally with the provider proxyConnectTimeout option. |
nginx.ingress.kubernetes.io/proxy-send-timeout | Can also be set globally with the provider proxySendTimeout option. |
nginx.ingress.kubernetes.io/proxy-read-timeout | Can also be set globally with the provider proxyReadTimeout option. |
Retry
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/proxy-next-upstream | Retries are not guaranteed to be sent to a different backend. Can also be set globally with the provider proxyNextUpstream option. |
nginx.ingress.kubernetes.io/proxy-next-upstream-tries | 0 is capped to the number of available servers to avoid infinite retries. Can also be set globally with the provider proxyNextUpstreamTries option. |
nginx.ingress.kubernetes.io/proxy-next-upstream-timeout | Can also be set globally with the provider proxyNextUpstreamTimeout option. |
ModSecurity / WAF
The ModSecurity annotations in this section are supported only in Traefik Hub API Gateway. They are not supported in Traefik Proxy.
| Annotation | Limitations / Notes |
|---|---|
nginx.ingress.kubernetes.io/enable-modsecurity | Opt-in/opt-out per ingress (see behavior below). |
nginx.ingress.kubernetes.io/modsecurity-snippet | Per-ingress directive snippet. Replaces the default snippet; does not concatenate. |
nginx.ingress.kubernetes.io/enable-owasp-core-rules | Enables the OWASP Core Rule Set (CRS). |
nginx.ingress.kubernetes.io/modsecurity-transaction-id | Custom transaction ID with NGINX variable interpolation. |
ModSecurity support is backed by Coraza, an open-source, ModSecurity-compatible WAF engine.
It must be activated at the provider level through providers.kubernetesIngressNGINX.modsec.* option. See the provider configuration for more information.
Opt-out model (modsec block configured):
When the modsec configuration block is set (e.g. modsec.snippet or modsec.OWASPCoreRules), ModSecurity is enabled for all ingresses by default.
Individual ingresses can disable it by setting enable-modsecurity: "false".
Opt-in model (no modsec block):
When no modsec configuration is set at the provider level, ModSecurity is turned off by default.
Individual ingresses opt in by setting enable-modsecurity: "true".
When no snippet is provided at all (neither provider-level nor per-ingress), the built-in modsecurity.conf-recommended configuration is used as a fallback.
This default configuration sets SecRuleEngine DetectionOnly, meaning requests are logged but not blocked.
To actively block requests, provide a custom snippet with SecRuleEngine On.
The Coraza engine does not support every ModSecurity directive.
The following directives from the standard modsecurity.conf-recommended are not compatible with Coraza and should be removed from custom snippets:
SecRequestBodyJsonDepthLimitSecAuditLogRelevantStatusSecUnicodeMapFileSecStatusEngine
Example: Enable ModSecurity and OWASP Core Rules on a single ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: whoami-nginx-ingress
namespace: apps
annotations:
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
nginx.ingress.kubernetes.io/enable-owasp-core-rules: "true"
nginx.ingress.kubernetes.io/modsecurity-snippet: |
SecRuleEngine On
spec:
ingressClassName: nginx
rules:
- host: whoami.docker.localhost
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
You can verify the WAF blocks malicious traffic with, for example:
curl -i "http://whoami.docker.localhost/?cmd=;cat%20/etc/passwd"
curl -i "http://whoami.docker.localhost/?input=1%20OR%201=1"
Caveats and Key Behavioral Differences
- Authentication: NGINX uses auth subrequests. Traefik Hub API Gateway forwards the original request to the auth service instead, so NGINX auth cache and keepalive annotations are not supported.
- Session Affinity: Sticky sessions use persistent cookies when
affinityis enabled. Only persistent affinity mode is supported. Canary affinity behavior is not configurable. - Leader Election: Not supported; no cluster mode with leader election.
- Default Backend: The
default-backendannotation and Ingress specdefaultBackendare both supported. The annotation must reference a service in the same namespace. - Load Balancing: The
load-balanceannotation only supportsround_robin. Useupstream-hash-bywhen you need request-hash-based backend selection. - CORS: NGINX responds with all configured headers unconditionally; Traefik Hub API Gateway handles headers differently between pre-flight and regular requests.
- TLS/Backend Protocols: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik Hub API Gateway.
- Path Handling: Traefik Hub API Gateway preserves trailing slashes by default; NGINX removes them unless configured otherwise.
- Retry: Unlike NGINX, Traefik Hub API Gateway does not guarantee that a retry will target a different backend.
- Rate Limiting: Traefik Hub API Gateway uses token bucket semantics and returns
429when the limit is exceeded, rather than NGINX's default503.
Unsupported NGINX Annotations
| Annotation | Notes |
|---|---|
nginx.ingress.kubernetes.io/affinity-canary-behavior | Canary affinity behavior is not configurable. |
nginx.ingress.kubernetes.io/auth-tls-verify-depth | |
nginx.ingress.kubernetes.io/auth-tls-error-page | |
nginx.ingress.kubernetes.io/auth-tls-match-cn | |
nginx.ingress.kubernetes.io/auth-cache-key | |
nginx.ingress.kubernetes.io/auth-cache-duration | |
nginx.ingress.kubernetes.io/auth-keepalive | |
nginx.ingress.kubernetes.io/auth-keepalive-share-vars | |
nginx.ingress.kubernetes.io/auth-keepalive-requests | |
nginx.ingress.kubernetes.io/auth-keepalive-timeout | |
nginx.ingress.kubernetes.io/auth-proxy-set-headers | |
nginx.ingress.kubernetes.io/enable-global-auth | |
nginx.ingress.kubernetes.io/disable-proxy-intercept-errors | |
nginx.ingress.kubernetes.io/limit-rate-after | |
nginx.ingress.kubernetes.io/limit-rate | |
nginx.ingress.kubernetes.io/limit-whitelist | |
nginx.ingress.kubernetes.io/limit-burst-multiplier | |
nginx.ingress.kubernetes.io/limit-connections | |
nginx.ingress.kubernetes.io/global-rate-limit | |
nginx.ingress.kubernetes.io/global-rate-limit-window | |
nginx.ingress.kubernetes.io/global-rate-limit-key | |
nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs | |
nginx.ingress.kubernetes.io/preserve-trailing-slash | Traefik Hub API Gateway preserves trailing slashes by default. |
nginx.ingress.kubernetes.io/proxy-cookie-domain | |
nginx.ingress.kubernetes.io/proxy-cookie-path | |
nginx.ingress.kubernetes.io/proxy-redirect-from | |
nginx.ingress.kubernetes.io/proxy-redirect-to | |
nginx.ingress.kubernetes.io/proxy-ssl-ciphers | |
nginx.ingress.kubernetes.io/proxy-ssl-verify-depth | |
nginx.ingress.kubernetes.io/proxy-ssl-protocols | |
nginx.ingress.kubernetes.io/enable-rewrite-log | |
nginx.ingress.kubernetes.io/satisfy | |
nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none | |
nginx.ingress.kubernetes.io/session-cookie-change-on-failure | |
nginx.ingress.kubernetes.io/ssl-ciphers | |
nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers | |
nginx.ingress.kubernetes.io/connection-proxy-header | |
nginx.ingress.kubernetes.io/enable-access-log | |
nginx.ingress.kubernetes.io/enable-opentracing | |
nginx.ingress.kubernetes.io/opentracing-trust-incoming-span | |
nginx.ingress.kubernetes.io/enable-opentelemetry | |
nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span | |
nginx.ingress.kubernetes.io/mirror-request-body | |
nginx.ingress.kubernetes.io/mirror-target | |
nginx.ingress.kubernetes.io/mirror-host | |
nginx.ingress.kubernetes.io/denylist-source-range | |
nginx.ingress.kubernetes.io/stream-snippet |
