TL;DR — Quick Summary
Complete guide to Traefik middlewares: configure HTTP routing, rate limiting, security headers, authentication, and circuit breakers for production.
Traefik Middlewares: HTTP Routing, Rate Limiting, and Security Guide
Traefik middlewares are the core mechanism for transforming HTTP requests and responses between the edge and your upstream services. Whether you need to enforce rate limits, inject security headers, strip path prefixes, or delegate authentication to an external service, each behavior is a discrete middleware object you attach to a router. This guide covers every major middleware category with configuration examples, a comparison against alternatives, and a complete production Docker Compose example.
Prerequisites
- Traefik v2.x or v3.x running as a Docker container or on a host
- Basic familiarity with Docker Compose labels
- A registered domain with DNS pointing to your server
- Optional: Authelia or OAuth2-Proxy for ForwardAuth flows
Traefik Architecture: The Request Pipeline
Traefik organizes traffic through four primitives:
- Entrypoints — network ports where traffic arrives (
:80,:443) - Routers — match incoming requests by host, path, or headers and direct them to a service
- Middlewares — transform the request or response (or reject it) before it reaches the service
- Services — the upstream containers or servers receiving the final request
Traefik discovers these objects automatically from Docker labels, file providers (traefik.yml or dynamic/), or Kubernetes CRDs (IngressRoute, Middleware). This auto-discovery is what makes Traefik an edge router rather than a static proxy.
Middleware Configuration Providers
Docker Labels
labels:
- "traefik.http.middlewares.my-headers.headers.stsSeconds=31536000"
- "traefik.http.routers.myapp.middlewares=my-headers"
File Provider (dynamic YAML)
# dynamic/middlewares.yml
http:
middlewares:
my-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
Kubernetes CRD
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: my-headers
spec:
headers:
stsSeconds: 31536000
Security Headers Middleware
The Headers middleware injects or modifies HTTP headers in both directions.
http:
middlewares:
secure-headers:
headers:
customRequestHeaders:
X-Forwarded-Proto: "https"
customResponseHeaders:
X-Powered-By: ""
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
permissionsPolicy: "camera=(), microphone=(), geolocation=()"
contentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'"
accessControlAllowOrigin: "https://example.com"
accessControlAllowMethods: "GET,POST,OPTIONS"
customRequestHeaders modifies what reaches the backend. customResponseHeaders modifies what reaches the client. Setting a header to an empty string removes it entirely.
Rate Limiting
The RateLimit middleware limits how frequently a client can make requests.
http:
middlewares:
api-rate-limit:
rateLimit:
average: 100
burst: 50
period: 1m
sourceCriterion:
requestHeaderName: "X-API-Key"
- average — sustained requests allowed per
period - burst — maximum spike above average
- period — rolling window (default
1s; supports1m,1h) - sourceCriterion — what identifies a “client”:
requestHeaderName— group by a header value (useful for API keys)requestHost: true— group by theHostheaderipStrategy.depth— use the Nth IP inX-Forwarded-Forto handle proxies
For edge deployments behind a CDN, set ipStrategy.depth: 1 so Traefik uses the real client IP rather than the CDN’s IP.
BasicAuth and DigestAuth
Protect routes with HTTP authentication when SSO is unavailable.
http:
middlewares:
basic-auth:
basicAuth:
users:
- "admin:$apr1$xyz$hashedpassword"
usersFile: "/etc/traefik/.htpasswd"
realm: "Admin Area"
removeHeader: true
Generate password hashes with:
htpasswd -nb admin mysecretpassword
removeHeader: true strips the Authorization header before forwarding, preventing credential leakage to the upstream service. DigestAuth follows the same structure with the digestAuth key.
ForwardAuth: SSO and OAuth2 Integration
ForwardAuth delegates the authentication decision to an external service such as Authelia or OAuth2-Proxy.
http:
middlewares:
sso-auth:
forwardAuth:
address: "http://authelia:9091/api/verify"
trustForwardHeader: true
authResponseHeaders:
- "Remote-User"
- "Remote-Groups"
- "Remote-Name"
- "Remote-Email"
Traefik sends every incoming request to address first. If that service returns 2xx, the original request proceeds. Any other status code is returned to the client as-is.
authResponseHeaders propagates identity headers from the auth response into the upstream request — this is how the backend knows who the user is without managing sessions itself.
Path Manipulation Middlewares
StripPrefix
Removes a prefix before forwarding, useful when the upstream expects a root path.
http:
middlewares:
strip-api:
stripPrefix:
prefixes:
- "/api/v1"
forceSlash: true
A request to /api/v1/users becomes /users at the upstream.
AddPrefix
Adds a prefix. Useful when multiple routes map to the same service with different path roots.
http:
middlewares:
add-v2:
addPrefix:
prefix: "/v2"
ReplacePath and ReplacePathRegex
http:
middlewares:
legacy-redirect:
replacePathRegex:
regex: "^/old/(.*)"
replacement: "/new/$1"
ReplacePath replaces the full path with a static string. ReplacePathRegex uses a capture group pattern for dynamic rewrites.
Compression
http:
middlewares:
compress:
compress:
excludedContentTypes:
- "image/jpeg"
- "image/png"
minResponseBodyBytes: 1024
Traefik supports both gzip and brotli. Brotli is used when the client sends Accept-Encoding: br. Content types in excludedContentTypes bypass compression.
Retry and CircuitBreaker
Retry
http:
middlewares:
retry-upstream:
retry:
attempts: 3
initialInterval: "100ms"
Traefik retries failed requests up to attempts times with exponential backoff starting at initialInterval.
CircuitBreaker
http:
middlewares:
circuit-breaker:
circuitBreaker:
expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.30"
checkPeriod: "10s"
fallbackDuration: "30s"
recoveryDuration: "10s"
The circuit opens when expression evaluates to true, blocking traffic for fallbackDuration. After that, it enters a half-open state for recoveryDuration before fully recovering.
IPAllowList
Restrict access to a whitelist of IP ranges.
http:
middlewares:
internal-only:
ipAllowList:
sourceRange:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
ipStrategy:
depth: 2
ipStrategy.depth controls which IP in the X-Forwarded-For chain is evaluated, essential when traffic passes through load balancers.
Redirect Middlewares
RedirectScheme (HTTP → HTTPS)
http:
middlewares:
https-redirect:
redirectScheme:
scheme: "https"
permanent: true
RedirectRegex (www → non-www)
http:
middlewares:
www-redirect:
redirectRegex:
regex: "^https://www\\.(.+)"
replacement: "https://${1}"
permanent: true
Advanced Middlewares
InFlightReq
Caps concurrent requests per source to prevent thundering-herd overload:
http:
middlewares:
in-flight:
inFlightReq:
amount: 20
sourceCriterion:
ipStrategy:
depth: 1
Buffering
Buffers the full request body before forwarding, protecting upstreams from slow-loris attacks and enabling retries on 5xx responses:
http:
middlewares:
buffer:
buffering:
maxRequestBodyBytes: 10485760
retryExpression: "IsNetworkError() && Attempts() < 2"
PassTLSClientCert
Passes the client TLS certificate as a header to the upstream — required for mTLS service-to-service auth:
http:
middlewares:
pass-cert:
passTLSClientCert:
pem: true
Middleware Chains
Chains let you compose multiple middlewares into a single named pipeline:
http:
middlewares:
secure-stack:
chain:
middlewares:
- secure-headers
- https-redirect
- api-rate-limit
- sso-auth
Attach the chain to a router with one label:
labels:
- "traefik.http.routers.myapp.middlewares=secure-stack"
Middleware Comparison: Traefik vs Alternatives
| Feature | Traefik | Nginx (modules) | Envoy (filters) | Kong (plugins) | Caddy (handlers) |
|---|---|---|---|---|---|
| Rate limiting | Built-in | limit_req module | ratelimit filter | Rate Limiting plugin | rate_limit handler |
| Auth delegation | ForwardAuth | auth_request | ext_authz | OIDC plugin | forward_auth |
| Circuit breaker | Built-in | Not native | Built-in | Plugin required | Not native |
| Path rewriting | Built-in | rewrite | rewrite filter | Built-in | rewrite |
| Compression | Built-in | gzip module | compressor | Not native | Built-in |
| Dynamic config | Auto (labels/CRD) | Requires reload | xDS API | Admin API | Auto (Caddyfile) |
| mTLS passthrough | PassTLSClientCert | ssl_client_cert | Built-in | Plugin required | tls handler |
Production Docker Compose Example
You have a production API service that needs HTTPS, rate limiting, security headers, and BasicAuth on the admin path.
services:
traefik:
image: traefik:v3.1
restart: unless-stopped
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.file.directory=/etc/traefik/dynamic
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.email=ops@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./dynamic:/etc/traefik/dynamic:ro
networks:
- edge
api:
image: ghcr.io/example/api:latest
restart: unless-stopped
networks:
- edge
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=le"
- "traefik.http.routers.api.middlewares=secure-stack"
- "traefik.http.services.api.loadbalancer.server.port=8080"
# Security headers
- "traefik.http.middlewares.hdrs.headers.stsSeconds=31536000"
- "traefik.http.middlewares.hdrs.headers.frameDeny=true"
- "traefik.http.middlewares.hdrs.headers.contentTypeNosniff=true"
- "traefik.http.middlewares.hdrs.headers.browserXssFilter=true"
# Rate limit
- "traefik.http.middlewares.rl.rateLimit.average=120"
- "traefik.http.middlewares.rl.rateLimit.burst=40"
- "traefik.http.middlewares.rl.rateLimit.period=1m"
# Compress
- "traefik.http.middlewares.compress.compress=true"
# Chain
- "traefik.http.middlewares.secure-stack.chain.middlewares=hdrs,rl,compress"
networks:
edge:
external: true
The file provider at ./dynamic/ holds longer middleware definitions (auth credentials, circuit breaker expressions) that are impractical in labels.
Gotchas and Edge Cases
- Dollar signs in BasicAuth hashes must be doubled (
$$) in Docker labels because Docker Compose interpolates single$as a variable. - Middleware order in a chain matters. Apply redirects before auth; apply compression last.
- ForwardAuth latency adds one extra HTTP round-trip per request. Cache auth decisions in Authelia or OAuth2-Proxy to minimize impact.
- RateLimit counters are per-replica. A deployment with three Traefik replicas effectively multiplies the allowed rate by three unless you use a shared store.
- StripPrefix with forceSlash: false may cause 404s on upstreams that expect a trailing slash. Test with both values.
- CircuitBreaker expressions use Traefik’s built-in expression language, not Prometheus PromQL. Refer to the Traefik docs for
ResponseCodeRatio,NetworkErrorRatio, andLatencyAtQuantileMS.
Summary
- Traefik middlewares attach to routers and transform requests before they reach upstream services
- Use
Headersfor HSTS, CSP, and CORS; always remove sensitive upstream headers RateLimitwithsourceCriterionhandles API throttling; setipStrategy.depthbehind CDNsForwardAuthintegrates cleanly with Authelia and OAuth2-Proxy for SSO without modifying application codeStripPrefix,ReplacePath, andAddPrefixhandle path normalization for multi-tenant routingCircuitBreakerandRetryprovide upstream resilience; combine them withBufferingfor slow-upload protectionChainmiddleware composes reusable security pipelines attachable to any router with one label- In Docker labels, double all
$characters in bcrypt/htpasswd hashes