- Published on
Traefik Advanced — Middleware Design, TLS Options, Multi-Provider
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction
- Middleware Fundamentals
- Middleware Chain Design Patterns
- Implementing SSO with forwardAuth
- Fine-Grained TLS Control with TLSOption
- mTLS and TLSStore
- Advanced Service Features
- Multi-Provider Composition
- Mapping to the Gateway API
- Yaegi Plugins
- Canary and Blue-Green Deployments
- Observability: Metrics and Tracing
- Production Pitfalls
- Troubleshooting
- Conclusion: Production Checklist
- References
Introduction
When you use Traefik purely as a basic reverse proxy and then requirements start piling up — wanting authentication applied uniformly across every route, accepting only certain clients via mTLS, sending just 5 percent of traffic to a new version — the default configuration quickly hits its limits. This article covers the next stage: the advanced features you need when running Traefik seriously in production.
A typical real-world situation looks like this. As microservices grow into the dozens, duplicating authentication logic per service becomes inefficient. At the same time, the security team requires mutual TLS authentication for external partner integrations, and the platform team wants to standardize canary and blue-green deployments for zero-downtime releases. All of these needs are solved through Traefik middleware, TLSOption, advanced service features, and multi-provider composition.
As of 2026, the Kubernetes Ingress API is effectively frozen. No new features are being added to Ingress, and the center of gravity is shifting toward its successor standard, the Gateway API. Traefik supports both its own IngressRoute CRD and a Gateway API provider, so we will also examine how the advanced patterns in this article map onto Gateway API filter and policy resources.
The goal here is not a mere feature list but conveying a sense of design — when and how to combine each feature. Therefore, each conceptual explanation is followed by CRD YAML and commands you can actually apply.
Middleware Fundamentals
Middleware is a processing unit that intervenes before a request reaches the backend service, or before a response returns to the client, to transform behavior. By separating cross-cutting concerns such as header injection, authentication, rate limiting, path rewriting, and compression from route definitions, it makes them reusable.
In Traefik, middleware is declared with the Kubernetes Middleware CRD and referenced in IngressRoute rules. The key point is that middleware applies as an ordered chain. If a middleware earlier in the chain rejects a request, later middleware and the backend are never invoked.
Request flow
Client
|
v
[EntryPoint :websecure]
|
v
[Router match] --- Host(api.example.com) && PathPrefix(/v1)
|
v
[Middleware chain]
1. rate-limit (rate limiting)
2. forward-auth (SSO authentication)
3. strip-prefix (path rewriting)
4. secure-headers (security headers)
|
v
[Service] --- weighted / mirroring / sticky
|
v
Pod (backend)
The most important principle in middleware design is ordering. Placing rate limiting before authentication means failed-auth traffic also counts toward the rate limit, which helps reduce the attack surface. Conversely, path rewriting should come after authentication so the backend receives the path it expects.
Middleware Chain Design Patterns
Listing several middleware on every route invites mistakes and duplication. Traefik provides the chain middleware type, letting you bundle frequently used combinations under a single name.
First, define the individual middleware.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: edge
spec:
rateLimit:
average: 100
burst: 50
period: 1s
sourceCriterion:
ipStrategy:
depth: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: secure-headers
namespace: edge
spec:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
customResponseHeaders:
X-Robots-Tag: "noindex, nofollow"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: strip-api-prefix
namespace: edge
spec:
stripPrefix:
prefixes:
- /v1
Now bundle them into a chain.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: api-edge-chain
namespace: edge
spec:
chain:
middlewares:
- name: rate-limit
- name: forward-auth-sso
- name: strip-api-prefix
- name: secure-headers
The route only needs to reference the single chain.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-route
namespace: edge
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`) && PathPrefix(`/v1`)
kind: Rule
middlewares:
- name: api-edge-chain
services:
- name: api-backend
port: 8080
tls:
secretName: api-example-com-tls
The advantages of this structure are clear. Managing common policy in one place means a change to the security-header policy propagates to all routes instantly. Route definitions also become concise, improving readability and review efficiency.
Implementing SSO with forwardAuth
The forwardAuth pattern delegates the authentication decision to a central auth server. Before sending a request to the backend, Traefik forwards a copy to the designated auth server; if the auth server returns a 2xx, the request passes, otherwise the auth server response is returned to the client as-is.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: forward-auth-sso
namespace: edge
spec:
forwardAuth:
address: http://auth-service.auth.svc.cluster.local:4180/oauth2/auth
trustForwardHeader: true
authResponseHeaders:
- X-Auth-Request-User
- X-Auth-Request-Email
- X-Auth-Request-Groups
authRequestHeaders:
- Cookie
- Authorization
The authResponseHeaders setting is the crux. When the auth server returns user information in headers as the verification result, Traefik extracts only those headers and forwards them to the backend. The backend receives trustworthy user identity information without performing token verification itself.
Combined with an auth gateway such as oauth2-proxy, this completes OIDC-based SSO. You must route a separate sign-in path so that oauth2-proxy can redirect unauthenticated requests to the login page.
forwardAuth sequence
Client --(1) request--> Traefik
Traefik --(2) verify /oauth2/auth--> auth-service
auth-service --(3a) 200 OK + user headers--> Traefik --(4) call backend--> Backend
auth-service --(3b) 401/302--> Traefik --(5) return same response--> Client
Setting trustForwardHeader to true trusts the X-Forwarded-* headers, which should only be enabled when the layer in front of Traefik is trustworthy. For an entrypoint exposed directly to the public, Traefik would blindly trust headers the client forged, so caution is required.
Fine-Grained TLS Control with TLSOption
Default TLS settings are often insufficient. One route may need to allow only TLS 1.3, while another opens a broader cipher suite for legacy clients. The TLSOption CRD handles this fine-grained control.
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: modern-tls
namespace: edge
spec:
minVersion: VersionTLS13
sniStrict: true
alpnProtocols:
- h2
- http/1.1
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: intermediate-tls
namespace: edge
spec:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
curvePreferences:
- CurveP521
- CurveP384
minVersion enforces the minimum TLS version, and setting sniStrict to true rejects connections without SNI. Note that cipherSuites settings are ignored under TLS 1.3. Cipher suite control is only meaningful in the TLS 1.2 range, so to apply a modern security policy it is simpler and safer to raise minVersion to VersionTLS13.
A route references a TLSOption as follows.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: secure-route
namespace: edge
spec:
entryPoints:
- websecure
routes:
- match: Host(`secure.example.com`)
kind: Rule
services:
- name: secure-backend
port: 8443
tls:
secretName: secure-example-com-tls
options:
name: modern-tls
namespace: edge
mTLS and TLSStore
Mutual TLS authentication requires not only the server but also the client to present a certificate. It is common in external partner integrations, service-to-service secure communication, and zero-trust environments. It is configured in the clientAuth section of TLSOption.
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: mtls-required
namespace: edge
spec:
minVersion: VersionTLS13
clientAuth:
secretNames:
- partner-ca-bundle
clientAuthType: RequireAndVerifyClientCert
The behavior changes significantly depending on the clientAuthType value. The table below summarizes it.
| clientAuthType | Requires client cert | Performs verification | When cert absent |
|---|---|---|---|
| NoClientCert | No | No | Pass |
| RequestClientCert | Requests only | No | Pass |
| RequireAnyClientCert | Yes | No | Reject |
| VerifyClientCertIfGiven | Optional | Verifies if presented | Pass |
| RequireAndVerifyClientCert | Yes | Always verifies | Reject |
For true mTLS in production, you must choose RequireAndVerifyClientCert. RequestClientCert performs no verification, so it is suitable only for collecting certificates.
TLSStore is the resource that defines a default certificate. It specifies a fallback certificate to use when no certificate matches the SNI. The cluster-wide default is set with a TLSStore named default.
apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
name: default
namespace: edge
spec:
defaultCertificate:
secretName: wildcard-example-com-tls
Without this configuration, Traefik may present a self-signed default certificate for unmatched SNI requests, triggering browser warnings.
Advanced Service Features
A Traefik service can express more than a simple backend designation. Weighted round-robin, mirroring, and sticky sessions are the prime examples.
Weighted Routing
Distributes traffic across multiple backends by ratio. It is the foundation of canary deployments.
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: api-weighted
namespace: edge
spec:
weighted:
services:
- name: api-stable
port: 8080
weight: 90
- name: api-canary
port: 8080
weight: 10
Giving stable a weight of 90 and canary a weight of 10 sends roughly 10 percent of traffic to the new version. The IngressRoute references this TraefikService just like an ordinary service.
Mirroring
Replicates a copy of real traffic to a separate backend. Because the response is discarded, you can test a new version with live traffic without affecting the client.
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: api-mirrored
namespace: edge
spec:
mirroring:
name: api-stable
port: 8080
mirrors:
- name: api-shadow
port: 8080
percent: 20
The setting above replicates 20 percent of traffic to api-shadow. Shadow traffic testing is a powerful tool for safely verifying how a new version behaves under real load patterns.
Sticky Sessions
Pins the same client to the same backend. It is required for legacy applications that hold session state in memory.
apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
name: api-sticky
namespace: edge
spec:
weighted:
services:
- name: api-stable
port: 8080
weight: 1
sticky:
cookie:
name: traefik_backend
secure: true
httpOnly: true
sameSite: strict
When possible, designing the application to be stateless and eliminating dependence on sticky sessions is preferable for scalability and resilience.
Multi-Provider Composition
Traefik has a multi-provider architecture that reads multiple configuration sources simultaneously. You can enable Kubernetes CRD, Kubernetes Ingress, file, Consul, Gateway API, and more together.
providers:
kubernetesCRD:
allowCrossNamespace: false
allowExternalNameServices: false
kubernetesGateway: {}
file:
directory: /etc/traefik/dynamic
watch: true
Each provider creates routers and services with its own namespace prefix. A viable strategy is to use CRD-based routing primarily in one cluster, statically define cluster-external legacy services through the file provider, and progressively adopt the Gateway API in parallel for the new standard.
| Provider | Primary use | Dynamic update | Notes |
|---|---|---|---|
| kubernetesCRD | Traefik-native IngressRoute etc. | Yes | Most feature-rich |
| kubernetesGateway | Gateway API standard | Yes | Future standard, highly portable |
| kubernetesIngress | Standard Ingress compatibility | Yes | API frozen, no new features |
| file | External static definitions | Yes (watch) | Useful for legacy integration |
A caveat with multi-provider use is router priority. If different providers define overlapping hosts, conflicts can arise based on priority rules, so it is best to clearly separate host and path namespaces per provider.
Mapping to the Gateway API
Given the trajectory in 2026, it is important to know the migration path to the Gateway API. Many of Traefik advanced middleware features map onto Gateway API filters and policy resources.
| Traefik feature | Gateway API equivalent |
|---|---|
| Middleware (header manipulation) | RequestHeaderModifier filter on HTTPRoute |
| Middleware (path rewriting) | URLRewrite filter on HTTPRoute |
| Middleware (redirect) | RequestRedirect filter on HTTPRoute |
| weighted TraefikService | weight on HTTPRoute backendRefs |
| TLSOption | TLS settings and policy on a Gateway listener |
| forwardAuth | ExtAuth-related extension (implementation-specific) |
Note that the core Gateway API specification does not yet cover all advanced middleware such as forwardAuth or rate limiting as a standard. Such features are handled through implementation-specific extensions or the ExtensionRef filter, and Traefik also supports some by linking its own Middleware via ExtensionRef. Therefore, if you want full portability, use only features expressible with core filters, and accept implementation dependency for advanced features as a trade-off.
Yaegi Plugins
Traefik embeds a Go interpreter called Yaegi, allowing it to dynamically load middleware plugins written in Go without compilation. This is useful when you need to implement internal policy as code.
Declare the plugin in the static configuration.
experimental:
plugins:
customHeader:
moduleName: github.com/example/traefik-custom-header
version: v0.2.0
The core skeleton of a plugin is Go code like the following.
package customheader
import (
"context"
"net/http"
)
type Config struct {
HeaderName string `json:"headerName,omitempty"`
HeaderValue string `json:"headerValue,omitempty"`
}
func CreateConfig() *Config {
return &Config{}
}
type CustomHeader struct {
next http.Handler
name string
headerName string
headerValue string
}
func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
return &CustomHeader{
next: next,
name: name,
headerName: config.HeaderName,
headerValue: config.HeaderValue,
}, nil
}
func (c *CustomHeader) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
req.Header.Set(c.headerName, c.headerValue)
c.next.ServeHTTP(rw, req)
}
The declared plugin is referenced as a plugin type in a Middleware CRD.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: add-custom-header
namespace: edge
spec:
plugin:
customHeader:
headerName: X-Internal-Trace
headerValue: edge-gateway
Since Yaegi is an interpreter, it runs slower than compiled Go. A plugin that performs heavy computation on the hot path can affect latency, so we recommend separating performance-critical logic into a dedicated sidecar or a properly built middleware.
Canary and Blue-Green Deployments
Using the weighted service shown earlier, you can implement canary deployments easily. The key is to adjust weights gradually.
# Step 1: send only 5 percent to the canary
kubectl patch traefikservice api-weighted -n edge --type merge \
-p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":95},{"name":"api-canary","port":8080,"weight":5}]}}}'
# After observing metrics with no issues, raise to 25 percent
kubectl patch traefikservice api-weighted -n edge --type merge \
-p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":75},{"name":"api-canary","port":8080,"weight":25}]}}}'
# Final cutover: 100 percent
kubectl patch traefikservice api-weighted -n edge --type merge \
-p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":0},{"name":"api-canary","port":8080,"weight":100}]}}}'
Blue-green keeps two environments running in advance and switches the router service reference all at once. You either swap weights 0 and 100 instantly, or change the route service name from blue to green.
Canary progression curve
Traffic ratio
100% | ____ green/canary
| ___/
50% | ______/
| _____/
5% | _____/
0% |_/_______________________________ time
T0 T1 T2 T3 T4 T5
confirm metric/error-rate gate at each step
You must place a metric gate between each step. Defining an automatic or manual rollback procedure when indicators such as error rate, latency, and saturation exceed thresholds is the foundation of safe operations.
Observability: Metrics and Tracing
To operate advanced routing, you must make visible what flows where. Traefik natively supports Prometheus metrics and OpenTelemetry tracing.
metrics:
prometheus:
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
buckets:
- 0.1
- 0.3
- 1.2
- 5.0
tracing:
otlp:
http:
endpoint: http://otel-collector.observability.svc.cluster.local:4318/v1/traces
Enabling addServicesLabels lets you view request counts and latency separately per service, so during a canary deployment you can directly compare the performance of stable and canary. However, high label cardinality increases the Prometheus load, so in environments with very many routers, enable addRoutersLabels with care.
Tracing is especially useful for finding bottlenecks in request paths where an external call such as forwardAuth is involved. You can distinguish span by span whether the auth server response is slow or the backend is slow.
Production Pitfalls
Here are the pitfalls frequently encountered in real operations.
First, a missing middleware namespace reference. When referencing middleware from another namespace, the name alone will not find it. You must follow the provider-prefix format of name-at-namespace-at-provider, or place it in the same namespace. Cross-namespace references also depend on the allowCrossNamespace setting.
Second, cipherSuites being ignored under TLS 1.3. If a security scanner flags a specific cipher and adjusting cipherSuites changes nothing, in most cases it is because the connection is negotiating to TLS 1.3.
Third, the header-forgery risk of forwardAuth. Exposing an entrypoint directly to the public while trustForwardHeader is on lets clients forge headers such as X-Forwarded-User. Outside the trust boundary, you must place a middleware up front that strips authentication-related headers.
Fourth, the conflict between sticky sessions and zero-downtime deployment. If a sticky cookie is pinned to a specific pod, the session breaks when that pod terminates during a rolling update. You must design session draining together with graceful shutdown.
Fifth, the side effects of mirroring. If the mirror target is a backend that performs write operations, data may be modified twice. The mirror must always be read-only or in an isolated environment.
Troubleshooting
Establishing a fixed verification order speeds up diagnosis when problems arise.
# Confirm via the API that routers, middleware, and services registered correctly
kubectl port-forward -n edge deploy/traefik 8080:8080
curl -s http://localhost:8080/api/http/routers | jq '.[] | {name, status, rule}'
curl -s http://localhost:8080/api/http/middlewares | jq '.[] | {name, status}'
# Check CRD application state and events
kubectl describe ingressroute api-route -n edge
kubectl get events -n edge --sort-by=.lastTimestamp
# Check TLS handshake and certificate
openssl s_client -connect secure.example.com:443 -servername secure.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject
# Test a call with an mTLS client certificate
curl -v --cert client.crt --key client.key https://secure.example.com/health
If the dashboard API status field is not enabled, it indicates a configuration error. When a middleware is in a warning state, it is usually because the referenced target does not exist or the namespace does not match.
Temporarily raising the log level to DEBUG lets you trace router matching and middleware application in detail. However, log volume explodes in production, so be sure to revert it after diagnosis.
Conclusion: Production Checklist
Traefik advanced features are powerful, but combining them incorrectly leads to outages that are hard to debug. We recommend the checklist below for pre-deployment review.
- Is the middleware chain order as intended (rate limit, then auth, then rewrite, then security headers)?
- Is forwardAuth trustForwardHeader enabled only within the trust boundary?
- Is there a middleware up front on the public entrypoint that strips authentication-related headers?
- Is TLSOption minVersion set in line with the security policy (TLS 1.3 recommended)?
- Does the mTLS segment perform verification with RequireAndVerifyClientCert?
- Is a default certificate specified in TLSStore to prevent warnings on match failure?
- Is a metric gate and rollback procedure defined at each canary step?
- Is the mirror target read-only or isolated so there is no double data modification?
- When using sticky sessions, are graceful shutdown and session draining designed in?
- Is Prometheus label cardinality at a manageable level?
- Do Yaegi plugins avoid introducing hot-path latency?
- Are there no host/path conflicts among multiple providers?
- When migrating to the Gateway API, have you distinguished features replaceable by core filters from dependent ones?
Checking these items habitually elevates Traefik from a simple proxy into a trustworthy production gateway. As next steps, we recommend evaluating automated certificate issuance integration with cert-manager and a progressive migration to Gateway-API-based routing.
References
- Traefik Official Documentation
- Traefik Middleware Overview
- Traefik forwardAuth Middleware
- Traefik TLS Configuration
- Traefik Kubernetes CRD Provider
- Traefik Plugin Development Guide
- Kubernetes Ingress Concepts
- Gateway API Official Documentation
- cert-manager Official Documentation
- Helm Official Documentation