Skip to main content

Traefik Kubernetes Ingress NGINX Routing Configuration

Since v3.18

The Kubernetes Controller for Ingresses with NGINX annotations.

NGINX Ingress Controller Retirement

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.

Global configuration

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

AnnotationLimitations / 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-urlTraefik 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-signinRedirects to signin URL on 401 response.
nginx.ingress.kubernetes.io/auth-snippetRequires auth-url and the provider allowSnippetAnnotations option.
nginx.ingress.kubernetes.io/auth-methodRequires 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

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/ssl-redirectCannot opt-out per route if enabled globally.
nginx.ingress.kubernetes.io/force-ssl-redirectCannot opt-out per route if enabled globally.
nginx.ingress.kubernetes.io/ssl-passthroughSome 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-secretWhen validation fails, the rejection happens during the TLS handshake rather than returning a 400 Bad Request.
nginx.ingress.kubernetes.io/auth-tls-verify-clientWhen 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-upstreamRequires auth-tls-secret.

Session Affinity

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/affinity
nginx.ingress.kubernetes.io/affinity-modeOnly 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

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/load-balanceOnly round_robin is supported. Use upstream-hash-by for hash-based backend selection.
nginx.ingress.kubernetes.io/backend-protocolFCGI and AUTO_HTTP not supported.
nginx.ingress.kubernetes.io/service-upstream
nginx.ingress.kubernetes.io/upstream-hash-byUses Traefik Hub API Gateway's HRW strategy with minimal NGINX variable interpolation.
nginx.ingress.kubernetes.io/upstream-vhost
nginx.ingress.kubernetes.io/custom-headersAdds 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-backendThe 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-versionOnly "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-weightValues are clamped to the 0..canary-weight-total range.
nginx.ingress.kubernetes.io/canary-weight-totalMinimum value is 100.

CORS

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/enable-corsPartial 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

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/app-root
nginx.ingress.kubernetes.io/from-to-www-redirectDoes not support wildcard hosts.
nginx.ingress.kubernetes.io/use-regex
nginx.ingress.kubernetes.io/rewrite-target
nginx.ingress.kubernetes.io/x-forwarded-prefixOnly applied together with rewrite-target.
nginx.ingress.kubernetes.io/permanent-redirectDefaults to a 301 Moved Permanently status code.
nginx.ingress.kubernetes.io/permanent-redirect-codeOnly valid 3XX HTTP status codes are accepted.
nginx.ingress.kubernetes.io/temporal-redirectTakes precedence over permanent-redirect. Defaults to a 302 Found status code.
nginx.ingress.kubernetes.io/temporal-redirect-codeOnly valid 3XX HTTP status codes are accepted.
nginx.ingress.kubernetes.io/custom-http-errorsUses the annotation or global default backend to serve intercepted errors.
nginx.ingress.kubernetes.io/server-aliasConflicts with an existing Ingress host rule are skipped.
nginx.ingress.kubernetes.io/server-snippetRequires the provider allowSnippetAnnotations option.
nginx.ingress.kubernetes.io/configuration-snippetRequires the provider allowSnippetAnnotations option.

IP Allowlist / Whitelist

Client IP Strategy

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.

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/whitelist-source-range
nginx.ingress.kubernetes.io/allowlist-source-range

Rate Limiting

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/limit-rpsExceeding the limit returns 429 Too Many Requests instead of NGINX's default 503 Service Unavailable.
nginx.ingress.kubernetes.io/limit-rpmExceeding the limit returns 429 Too Many Requests instead of NGINX's default 503 Service Unavailable.

Buffering

AnnotationLimitations / 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-numberUsed with proxy-buffer-size to compute the total in-memory response buffer size.
nginx.ingress.kubernetes.io/proxy-max-temp-file-size

Timeout

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/proxy-connect-timeoutCan also be set globally with the provider proxyConnectTimeout option.
nginx.ingress.kubernetes.io/proxy-send-timeoutCan also be set globally with the provider proxySendTimeout option.
nginx.ingress.kubernetes.io/proxy-read-timeoutCan also be set globally with the provider proxyReadTimeout option.

Retry

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/proxy-next-upstreamRetries 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-tries0 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-timeoutCan also be set globally with the provider proxyNextUpstreamTimeout option.

ModSecurity / WAF

Traefik Hub API Gateway only

The ModSecurity annotations in this section are supported only in Traefik Hub API Gateway. They are not supported in Traefik Proxy.

AnnotationLimitations / Notes
nginx.ingress.kubernetes.io/enable-modsecurityOpt-in/opt-out per ingress (see behavior below).
nginx.ingress.kubernetes.io/modsecurity-snippetPer-ingress directive snippet. Replaces the default snippet; does not concatenate.
nginx.ingress.kubernetes.io/enable-owasp-core-rulesEnables the OWASP Core Rule Set (CRS).
nginx.ingress.kubernetes.io/modsecurity-transaction-idCustom 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.

Incompatible ModSecurity directives

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:

  • SecRequestBodyJsonDepthLimit
  • SecAuditLogRelevantStatus
  • SecUnicodeMapFile
  • SecStatusEngine

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 affinity is 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-backend annotation and Ingress spec defaultBackend are both supported. The annotation must reference a service in the same namespace.
  • Load Balancing: The load-balance annotation only supports round_robin. Use upstream-hash-by when 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 429 when the limit is exceeded, rather than NGINX's default 503.

Unsupported NGINX Annotations

AnnotationNotes
nginx.ingress.kubernetes.io/affinity-canary-behaviorCanary 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-slashTraefik 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