Loading...
Loading...
Compare original and translation side by side
┌─────────────────────────────────────────────────────────────────┐
│ PROVIDERS ENTRYPOINTS ROUTERS SERVICES │
│ ────────── ─────────── ─────── ──────── │
│ Docker labels → :80 (web) → Host rule → LB pool │
│ File provider → :443 (websecure) PathPrefix backend │
│ Kubernetes → :8080 (dashboard) Headers servers │
└─────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────┐
│ PROVIDERS ENTRYPOINTS ROUTERS SERVICES │
│ ────────── ─────────── ─────── ──────── │
│ Docker labels → :80 (web) → Host rule → LB pool │
│ File provider → :443 (websecure) PathPrefix backend │
│ Kubernetes → :8080 (dashboard) Headers servers │
└─────────────────────────────────────────────────────────────────┘traefik.ymltraefik.ymlundefinedundefined
---
---undefinedundefinedtraefik.example.comapi.example.comexample.comwww.example.com
---traefik.example.comapi.example.comexample.comwww.example.com
---undefinedundefined# HSTS + security headers
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
frameDeny: true
customResponseHeaders:
X-Powered-By: ""
Server: ""
# Rate limiting
rate-limit:
rateLimit:
average: 100 # Requests per second (average)
burst: 50 # Burst allowance
period: 1m # Window period
# Strip /api prefix before forwarding
strip-api-prefix:
stripPrefix:
prefixes:
- "/api"
# Add /v1 prefix
add-v1-prefix:
addPrefix:
prefix: "/v1"
# Basic auth
internal-auth:
basicAuth:
usersFile: /etc/traefik/users.htpasswd
removeHeader: true # Strip Authorization before passing to upstream
# IP whitelist (Traefik v3: use ipAllowList)
office-only:
ipAllowList:
sourceRange:
- "10.0.0.0/8"
- "203.0.113.42/32"
# Retry on failure
retry-middleware:
retry:
attempts: 3
initialInterval: 100ms
# www redirect
www-redirect:
redirectRegex:
regex: "^https?://www\\.example\\.com/(.*)"
replacement: "https://example.com/${1}"
permanent: true
# Circuit breaker
circuit-breaker:
circuitBreaker:
expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.25 || NetworkErrorRatio() > 0.10"
```yaml# HSTS + 安全头
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
frameDeny: true
customResponseHeaders:
X-Powered-By: ""
Server: ""
# 速率限制
rate-limit:
rateLimit:
average: 100 # 每秒请求数(平均值)
burst: 50 # 突发请求允许量
period: 1m # 统计窗口周期
# 转发前移除/api前缀
strip-api-prefix:
stripPrefix:
prefixes:
- "/api"
# 添加/v1前缀
add-v1-prefix:
addPrefix:
prefix: "/v1"
# 基础认证
internal-auth:
basicAuth:
usersFile: /etc/traefik/users.htpasswd
removeHeader: true # 转发给上游服务前移除Authorization头
# IP白名单(Traefik v3:使用ipAllowList)
office-only:
ipAllowList:
sourceRange:
- "10.0.0.0/8"
- "203.0.113.42/32"
# 失败重试
retry-middleware:
retry:
attempts: 3
initialInterval: 100ms
# www重定向
www-redirect:
redirectRegex:
regex: "^https?://www\\.example\\.com/(.*)"
replacement: "https://example.com/${1}"
permanent: true
# 熔断器
circuit-breaker:
circuitBreaker:
expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.25 || NetworkErrorRatio() > 0.10"
```yamllegacy.example.com
---legacy.example.com
---Request → rate-limit → ip-allowlist → strip-api-prefix → upstream
Response ← security-headers (applied on response) ←──────────────undefined请求 → rate-limit → ip-allowlist → strip-api-prefix → 上游服务
响应 ← security-headers(在响应阶段生效) ←──────────────undefined
The `@file` suffix means the middleware is defined in the file provider.
Use `@docker` for middlewares defined via labels on another container.
---
`@file`后缀表示中间件定义在文件提供者中。如果中间件通过其他容器的标签定义,使用`@docker`后缀。
---undefinedundefined*
---*
---| Anti-Pattern | Problem | Solution |
|---|---|---|
| Dashboard exposed on port 8080 with no auth | Set |
| Every container automatically gets a route, including databases | Always set |
| Wildcard cert with HTTP challenge | HTTP challenge cannot prove DNS control for wildcards | Use |
| Not pinning Traefik version | | Pin to exact version: |
Missing | Traefik picks wrong port arbitrarily | Always specify the service port label explicitly |
Storing | Certificates lost on container restart → rate limit hit | Mount |
| Applying heavy middlewares (auth, rate-limit) only on some routes | Inconsistent security posture | Apply security-headers globally via entrypoint middleware; add auth/rate-limit to sensitive routers |
| Router rules without priority on overlapping paths | Non-deterministic routing when multiple rules match | Add |
| Docker socket mounted as read-write with no protection | Container escape via Docker API | Use socket proxy (e.g., |
| No health checks on services | Traefik routes to unhealthy containers | Configure |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
生产环境中设置 | 仪表盘在8080端口暴露且无认证保护 | 设置 |
Docker提供者中设置 | 所有容器自动获得路由,包括数据库 | 始终设置 |
| 通配符证书使用HTTP挑战 | HTTP挑战无法证明通配符域名的DNS控制权 | 对通配符域名使用 |
| 不固定Traefik版本 | | 固定到具体版本: |
容器暴露多个端口时缺少 | Traefik会随机选择错误的端口 | 始终显式指定服务端口标签 |
| 容器重启后证书丢失,触发Let's Encrypt速率限制 | 将 |
| 仅在部分路由上应用重量级中间件(认证、速率限制) | 安全姿态不一致 | 通过入口点中间件全局应用安全头;在敏感路由上添加认证/速率限制中间件 |
| 路径重叠的路由规则未设置优先级 | 多个规则匹配时路由行为不确定 | 添加 |
| Docker套接字以读写权限挂载且无保护 | 可通过Docker API逃逸容器 | 使用套接字代理(如 |
| 服务未配置健康检查 | Traefik会将请求路由到不健康的容器 | 在负载均衡器上配置 |
| Symptom | Likely Cause | Fix |
|---|---|---|
| Router not matching — 404 from Traefik | Label typo, wrong entrypoint name, container not on Traefik network | Check |
| Certificate not issued | Port 80 blocked (HTTP challenge) or DNS credentials wrong (DNS challenge) | |
| Middleware not applied | | Use exact |
| "404 page not found" from upstream app | App routing issue, not Traefik | Bypass Traefik: |
| Dashboard shows router but requests still fail | Middleware blocking (auth, IP whitelist) | Check access log; temporarily remove middlewares to isolate |
| Let's Encrypt client refuses to read file | |
| TLS certificate is self-signed (Traefik default) | ACME not configured, or cert resolver name wrong on router | Ensure |
| Container restarts but keeps same cert | acme.json not updated | Ensure acme.json volume is persistent; delete acme.json and restart to force re-issue (if not rate-limited) |
| High memory usage | Too many access log entries buffered | Increase |
| Sticky sessions not working | Cookie not forwarded by load balancer | Ensure |
latestexposedByDefault: falseapi.insecure: falseacme.json600/var/run/docker.sock@providerlatestexposedByDefault: falseapi.insecure: falseacme.json600/var/run/docker.sock@provider