Traefik
Traefik est un reverse proxy et load balancer HTTP libre et open source, conçu spécifiquement pour les environnements dynamiques (Docker, Kubernetes, Swarm, Consul...). Sa particularité est de découvrir automatiquement les services à exposer via des labels ou des CRD, sans avoir besoin de recharger manuellement une configuration à chaque changement.
Source : Documentation officielle Traefik
Traefik vs Nginx vs Nginx Proxy Manager vs Caddy vs Zoraxy
| Critère | Traefik | Nginx | Nginx Proxy Manager | Caddy | Zoraxy |
|---|---|---|---|---|---|
| Configuration | YAML/labels, déclarative | Fichiers de config manuels | Interface web (au-dessus de Nginx) | Caddyfile ou JSON | Interface web |
| Découverte auto (Docker) | ✅ Native, via labels | ❌ Non | ❌ Non | ⚠️ Via plugin (caddy-docker-proxy) | ❌ Non |
| Certificats SSL auto | ✅ Native (ACME/Let's Encrypt) | ❌ Manuel (Certbot) | ✅ Intégré (Certbot en interne) | ✅ Native, activée par défaut | ✅ Intégré |
| Interface web / dashboard | ✅ Dashboard (lecture, + gestion via Traefik Manager) | ❌ Aucune (CLI/fichiers) | ✅ Complète (CRUD hôtes, certificats) | ❌ Aucune nativement | ✅ Complète |
| Courbe d'apprentissage | Moyenne (concepts propres : routers/services/middlewares) | Élevée (syntaxe puissante mais verbeuse) | Faible (tout en clic) | Faible (syntaxe très simple) | Faible |
| Performance brute | Bonne | Excellente (référence historique) | Bonne (basé sur Nginx) | Bonne | Correcte |
| Cas d'usage typique | Homelab/Docker avec beaucoup de conteneurs, IaC | Prod à forte charge, configs sur-mesure | Débutants, petit homelab, tout en GUI | Simplicité + HTTPS "zero-conf" | Alternative légère à NPM |
| Écosystème plugins | Riche (middlewares, plugins Yaegi comme CrowdSec) | Riche (modules natifs, souvent recompilation requise) | Limité (hérite de Nginx) | Modules Go (recompilation ou xcaddy) | Limité |
Pourquoi Traefik dans un contexte homelab/Docker ? C'est le seul de la liste à lire directement les labels des conteneurs Docker et à mettre à jour son routage en temps réel, sans redémarrage, dès qu'un conteneur démarre ou s'arrête. C'est cette intégration native qui en fait le choix de référence pour un hôte avec de nombreux services conteneurisés.
Concepts clés
Traefik repose sur quatre briques :
- EntryPoints : les points d'écoute réseau (ports), ex.
web(80) etwebsecure(443). - Routers : analysent la requête entrante (règle sur le
Host, le chemin, les headers...) et décident vers quel service la router. - Middlewares : modifient la requête ou la réponse avant/après le routage (auth, headers, redirection, rate limit, CrowdSec...).
- Services : la définition des backends réels vers lesquels le trafic est envoyé (load balancing entre plusieurs serveurs).
Client → EntryPoint (:443) → Router (Host match) → Middlewares → Service → Conteneur backendTraefik distingue également deux types de configuration :
- Configuration statique : chargée uniquement au démarrage (entrypoints, providers, certificats resolvers). Fichier
traefik.yml, arguments CLI ou variables d'environnement. - Configuration dynamique : rechargée à chaud, sans redémarrage (routers, services, middlewares). Labels Docker, fichiers dans
providers.file, ou API des CRD Kubernetes.
Installation avec Docker Compose
Architecture recommandée : un réseau Docker dédié (proxy) sur lequel Traefik et tous les services exposés sont connectés, un fichier de configuration statique, et un dossier de configuration dynamique pour les middlewares partagés (comme CrowdSec, voir plus bas).
Arborescence
traefik/
├── docker-compose.yml
├── traefik.yml # configuration statique
├── acme.json # stockage des certificats (créé vide, chmod 600)
└── dynamic/
└── middlewares.yml # configuration dynamique partagéeCréer le réseau externe
docker network create proxytraefik.yml (configuration statique)
api:
dashboard: true
insecure: false # le dashboard est exposé via un router, pas en direct
log:
level: INFO
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # un conteneur doit explicitement avoir traefik.enable=true
network: proxy
file:
directory: /etc/traefik/dynamic
watch: true
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /acme.json
httpChallenge:
entryPoint: web
accessLog:
filePath: "/logs/access.log"
format: json
bufferingSize: 100
filters:
statusCodes:
- "200-299"
- "400-599"
fields:
headers:
defaultMode: drop
names:
User-Agent: keep
X-Forwarded-For: keep
X-Real-Ip: keepdocker-compose.yml
services:
traefik:
image: traefik:v3.7
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./acme.json:/acme.json
- ./logs:/logs
networks:
- proxy
labels:
- "traefik.enable=true"
# Dashboard exposé via un router, protégé par auth basique
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$xxxxxxxx"
networks:
proxy:
external: trueLe mot de passe de l'auth basique se génère avec
htpasswd -nb admin motdepasse(paquetapache2-utils). Attention à doubler les$($$) dans ledocker-compose.yml, sinon Docker Compose tente de les interpréter comme des variables.
Créer le fichier acme.json avec les bonnes permissions
touch acme.json
chmod 600 acme.jsonSans ce chmod 600, Traefik refuse de démarrer le stockage ACME (le fichier contient les clés privées des certificats).
Démarrer Traefik
docker compose up -d
docker compose logs -f traefikAjouter un hôte (exposer un service)
Chaque conteneur à exposer doit être connecté au réseau proxy et porter les labels Traefik. Exemple avec un whoami de test :
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=letsencrypt"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
proxy:
external: trueAucun redémarrage de Traefik n'est nécessaire : dès que le conteneur whoami démarre, le provider Docker détecte les labels et crée le router, le service et déclenche la demande de certificat.
Règles de routage courantes
| Règle | Usage |
|---|---|
Host(`app.example.com`) | Correspondance sur le nom de domaine |
PathPrefix(`/api`) | Correspondance sur un préfixe de chemin |
Host(`app.example.com`) && PathPrefix(`/api`) | Combinaison de règles |
HostRegexp(`^.+\.example\.com$`) | Sous-domaines dynamiques (wildcard-like) |
ClientIP(`192.168.1.0/24`) | Restriction par IP source |
Load balancing entre plusieurs instances
labels:
- "traefik.http.services.monapp.loadbalancer.server.port=8080"
- "traefik.http.services.monapp.loadbalancer.healthcheck.path=/health"
- "traefik.http.services.monapp.loadbalancer.healthcheck.interval=10s"Avec plusieurs conteneurs (répliques) sur le même réseau et les mêmes labels de service, Traefik répartit automatiquement la charge entre eux.
Gestion des certificats SSL
Trois méthodes de validation ACME sont disponibles :
1. HTTP Challenge (le plus simple)
Nécessite que le port 80 soit accessible depuis Internet. Déjà configuré dans l'exemple traefik.yml ci-dessus (httpChallenge.entryPoint: web).
Une fois le certificatesResolvers.letsencrypt déclaré, ajouter un certificat pour un hôte se résume à deux labels sur le conteneur :
labels:
- "traefik.http.routers.monapp.rule=Host(`monapp.example.com`)"
- "traefik.http.routers.monapp.entrypoints=websecure"
- "traefik.http.routers.monapp.tls.certresolver=letsencrypt"Au démarrage du conteneur, Traefik détecte le router, constate qu'aucun certificat valide n'existe pour monapp.example.com et déclenche automatiquement la demande ACME auprès de Let's Encrypt (challenge HTTP sur le port 80) — le certificat est généré et stocké dans acme.json en quelques secondes, sans aucune commande à lancer manuellement (pas de certbot, pas de renouvellement à planifier).
Éviter de répéter certresolver sur chaque service
Pour ne pas avoir à ajouter tls.certresolver=letsencrypt sur tous les routers, il est possible de définir un résolveur par défaut sur l'entrypoint websecure dans traefik.yml :
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencryptChaque router utilisant l'entrypoint websecure avec TLS activé obtient alors automatiquement un certificat Let's Encrypt, sans avoir besoin du label tls.certresolver :
labels:
- "traefik.http.routers.monapp.rule=Host(`monapp.example.com`)"
- "traefik.http.routers.monapp.entrypoints=websecure"
- "traefik.http.routers.monapp.tls=true"2. TLS Challenge
Validation via TLS-ALPN-01 sur le port 443, sans exposer le port 80 :
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /acme.json
tlsChallenge: {}3. DNS Challenge (obligatoire pour les wildcards)
Permet de générer des certificats wildcard (*.example.com) sans exposer aucun port, via une validation par enregistrement TXT chez le registrar/fournisseur DNS. Exemple avec OVH :
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /acme.json
dnsChallenge:
provider: ovh
resolvers:
- "1.1.1.1:53"Variables d'environnement à fournir au conteneur Traefik (clés API OVH) :
environment:
- OVH_ENDPOINT=ovh-eu
- OVH_APPLICATION_KEY=xxxx
- OVH_APPLICATION_SECRET=xxxx
- OVH_CONSUMER_KEY=xxxxChaque fournisseur DNS supporté (Cloudflare, Gandi, OVH, Infomaniak...) a ses propres variables d'environnement, listées dans la documentation officielle des providers DNS.
Traefik renouvelle automatiquement les certificats environ 30 jours avant expiration. Le fichier acme.json contient l'ensemble des certificats et clés privées : à sauvegarder, jamais à versionner en clair.
Ajouter un hôte "classique" (IP + port, hors Docker)
Contrairement à Nginx Proxy Manager ou Zoraxy, Traefik n'a pas de formulaire "ajouter un proxy host" avec un champ IP + port : ce cas (service externe, VM, NAS, appareil physique... tout ce qui n'est pas un conteneur sur le réseau proxy) passe par le provider file, déjà déclaré dans traefik.yml (providers.file, dossier dynamic/, watch: true). Il suffit de déposer un fichier dans dynamic/ avec un router et un service pointant vers une URL fixe : c'est l'équivalent exact du "Proxy host" de Zoraxy/NPM où l'on saisit une IP et un port.
dynamic/hosts/nas.yml :
http:
routers:
nas:
rule: "Host(`nas.example.com`)"
entrypoints:
- websecure
tls:
certresolver: letsencrypt
service: nas
middlewares:
- crowdsec@file # optionnel, voir plus bas
services:
nas:
loadBalancer:
servers:
- url: "http://192.168.1.50:5000"
passHostHeader: trueLe fichier est repris à chaud (watch: true) : aucun redémarrage de Traefik n'est nécessaire. passHostHeader: true (valeur par défaut) transmet le Host d'origine au backend, comme le fait le champ "Requested Domain" côté Zoraxy.
Pour un backend en HTTPS avec certificat auto-signé (ex. interface d'administration d'un routeur, iDRAC, Proxmox...) :
http:
services:
proxmox:
loadBalancer:
servers:
- url: "https://192.168.1.10:8006"
serversTransport: insecure-backend
serversTransports:
insecure-backend:
insecureSkipVerify: truePlusieurs adresses IP peuvent être listées sous servers: pour du load balancing/failover vers plusieurs backends physiques, exactement comme pour des conteneurs (voir Load balancing entre plusieurs instances).
Support des WebSockets
Aucune configuration supplémentaire n'est nécessaire : Traefik proxifie nativement les upgrades HTTP/1.1 (Connection: Upgrade, Upgrade: websocket) dès qu'un router/service standard est déclaré, contrairement à Nginx qui exige des directives proxy_set_header Upgrade/Connection explicites. C'est la même transparence que le toggle "Websocket Support" de Zoraxy, activé par défaut.
Points d'attention pour les applications qui ouvrent des connexions WebSocket longues (ex. Home Assistant, code-server, Immich, Vaultwarden en mode Send temps réel...) :
http:
services:
ha:
loadBalancer:
servers:
- url: "http://192.168.1.20:8123"
# timeout de lecture sans borne pour ne pas couper les WS de longue durée
# (par défaut Traefik n'impose pas de timeout applicatif, seul l'entrypoint/serversTransport en a)Si les WebSockets sont coupés après un délai fixe, vérifier le timeout du serversTransport (forwardingTimeouts) et, le cas échéant, un reverse proxy/firewall en amont (ex. Cloudflare, box FAI) qui coupe les connexions inactives.
Bloquer les crawlers IA
Traefik n'a pas de middleware natif "bloquer par User-Agent" (contrairement au bloc IA prêt à l'emploi de Zoraxy) : il faut passer par un plugin communautaire, sur le même principe que le middleware CrowdSec déjà en place.
1. Activer le plugin dans la configuration statique
traefik.yml :
experimental:
plugins:
TmNoAiBotsPlugin:
moduleName: "github.com/edelbluth/tm_no_ai_bots"
version: "v0.2.4"2. Déclarer le middleware (configuration dynamique)
dynamic/middlewares.yml :
http:
middlewares:
no-ai-bots:
plugin:
TmNoAiBotsPlugin:
botPatterns:
- gptbot # OpenAI
- chatgpt
- openai
- ccbot # Common Crawl (alimente de nombreux LLM)
- anthropic # Anthropic
- claude
- claudebot
- google-extended # Google (entraînement Gemini)
- bytespider # ByteDance
- amazon
- perplexity
- omgili
- meta-extern # Meta
- cohereLe plugin répond 403 à toute requête dont le User-Agent contient un des motifs listés, et sert automatiquement un robots.txt cohérent (Disallow) pour les bots identifiés qui le respectent.
3. Appliquer le middleware aux hôtes à protéger
labels:
- "traefik.http.routers.monapp.middlewares=no-ai-bots@file"Ou, pour un hôte déclaré via le provider file (voir plus haut) :
http:
routers:
nas:
middlewares:
- no-ai-bots@fileLe middleware
no-ai-botspeut être combiné aveccrowdsec@file(middlewares: [no-ai-bots@file, crowdsec@file]) : la liste UA filtre les scrapers IA déclarés, CrowdSec couvre le reste (brute force, scan, CVE...).
D'autres plugins existent selon le besoin : Block User-Agent (blocage générique par regex sur le UA) ou Bot Wrangler (liste de bots IA maintenue via le projet ai.robots.txt, avec option de rediriger les bots vers un tarpit plutôt qu'un simple 403). L'ensemble des plugins Traefik est catalogué sur plugins.traefik.io.
Interface web (dashboard)
Le dashboard Traefik (accessible en lecture sur /dashboard/) permet de visualiser en temps réel :
- La liste des routers, services et middlewares actifs, avec leur état de santé
- Les entrypoints configurés
- Les certificats gérés et leur provider ACME
Il n'autorise pas nativement la création d'hôtes : contrairement à Nginx Proxy Manager, tout passe par les labels Docker ou les fichiers de configuration dynamique. Pour une gestion en GUI complète (formulaires de création de routes, édition de la config statique, suivi des expirations de certificats), il existe des projets tiers comme Traefik Manager, qui vient se brancher sur l'API Traefik et le provider file :
services:
traefik-manager:
image: ghcr.io/chr0nzz/traefik-manager:latest
restart: unless-stopped
networks:
- proxy
volumes:
- ./dynamic/traefik-manager.yml:/app/config/dynamic.yml
- ./traefik-manager-config:/app/config
- ./traefik-manager-backups:/app/backups
environment:
- COOKIE_SECURE=true
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-manager.rule=Host(`traefik-manager.example.com`)"
- "traefik.http.routers.traefik-manager.entrypoints=websecure"
- "traefik.http.routers.traefik-manager.tls.certresolver=letsencrypt"
- "traefik.http.services.traefik-manager.loadbalancer.server.port=5000"Logger les connexions (IP, appareil, hôte...)
Les access logs de Traefik journalisent chaque requête traitée. Le format json est recommandé car il permet un filtrage fin des champs et s'intègre facilement à des outils comme CrowdSec, Loki ou Grafana.
Principaux champs disponibles :
| Champ | Description |
|---|---|
ClientAddr / ClientHost | IP (et port) du client à l'origine de la requête |
RequestHost | Nom d'hôte demandé (Host header) |
RequestMethod / RequestPath | Méthode HTTP et chemin demandé |
RouterName | Router Traefik qui a traité la requête |
ServiceName | Service backend qui a répondu |
DownstreamStatus | Code HTTP retourné au client |
Duration / OriginDuration | Temps de traitement total / côté backend |
RequestHeader.User-Agent (via fields.headers) | Type d'appareil/navigateur du client |
Configuration déjà posée dans traefik.yml plus haut :
accessLog:
filePath: "/logs/access.log"
format: json
filters:
statusCodes:
- "200-299"
- "400-599"
fields:
headers:
defaultMode: drop
names:
User-Agent: keep # identifie l'appareil / le navigateur du client
X-Forwarded-For: keep # IP réelle du client derrière un éventuel proxy en amont
X-Real-Ip: keepAvec cette config, chaque ligne de /logs/access.log (au format JSON) contient l'IP précise du client, l'hôte demandé, le user-agent (donc le type d'appareil), le router/service ayant traité la requête, et le code de statut retourné — l'essentiel pour de l'audit ou pour alimenter un outil de détection comme CrowdSec.
Exploiter les logs
# Suivre les connexions en temps réel
tail -f logs/access.log | jq
# Filtrer par IP source
jq 'select(.ClientHost == "203.0.113.42")' logs/access.log
# Lister les hôtes les plus demandés
jq -r '.RequestHost' logs/access.log | sort | uniq -c | sort -rnIntégration de CrowdSec
CrowdSec est un IPS collaboratif : il analyse les logs (ici, les access logs de Traefik) pour détecter des comportements malveillants (brute force, scan, exploitation de CVE...) et bloque les IP identifiées via un bouncer. L'intégration avec Traefik se fait via un plugin qui agit comme middleware.
Architecture
Requête → Traefik (middleware bouncer) → API locale CrowdSec (LAPI) → décision (ban/allow) → Service backend
↑
Conteneur CrowdSec (analyse les access logs de Traefik)1. Déployer le conteneur CrowdSec
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
environment:
- GID=1000
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios
volumes:
- ./crowdsec/data:/var/lib/crowdsec/data
- ./crowdsec/etc:/etc/crowdsec
- ./logs:/var/log/traefik:ro
networks:
- proxycrowdsecurity/traefik: scénarios spécifiques aux menaces HTTP visibles via Traefik.crowdsecurity/http-cve: détection d'exploitation de CVE connues.crowdsecurity/base-http-scenarios: scénarios HTTP génériques (scan, brute force...).
2. Configurer l'acquisition des logs
crowdsec/etc/acquis.yaml :
filenames:
- /var/log/traefik/access.log
labels:
type: traefik3. Enregistrer le bouncer Traefik
docker exec crowdsec cscli bouncers add traefik-bouncerCette commande renvoie une clé API à conserver : elle sera utilisée par le plugin Traefik pour interroger CrowdSec.
4. Activer le plugin dans la configuration statique
traefik.yml :
experimental:
plugins:
bouncer:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.4.65. Déclarer le middleware CrowdSec (configuration dynamique)
dynamic/middlewares.yml :
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
crowdsecMode: live
crowdsecLapiHost: crowdsec:8080
crowdsecLapiScheme: http
crowdsecLapiKey: <CLE_API_DU_BOUNCER>
defaultDecisionSeconds: 60
remediationStatusCode: 403
forwardedHeadersTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
clientTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/166. Appliquer le middleware aux routers à protéger
Sur chaque service exposé (ex. whoami plus haut) :
labels:
- "traefik.http.routers.whoami.middlewares=crowdsec@file"Toute IP jugée malveillante par CrowdSec (brute force, scan de vulnérabilités, tentative d'exploitation de CVE...) reçoit alors une réponse 403 sur l'ensemble des routes protégées par ce middleware, sans que la requête n'atteigne le conteneur backend.
Gestion des décisions (bans)
# Lister les IP actuellement bloquées
docker exec crowdsec cscli decisions list
# Bloquer manuellement une IP
docker exec crowdsec cscli decisions add --ip 203.0.113.42 --type ban --duration 10m
# Débloquer une IP
docker exec crowdsec cscli decisions delete --ip 203.0.113.42
# Lister les alertes générées
docker exec crowdsec cscli alerts listProtection complémentaire au niveau de l'hôte
Le bouncer Traefik protège les applications, mais pas l'hôte Docker lui-même (ex. SSH). Un bouncer firewall complète l'ensemble en bannissant les IP au niveau iptables/nftables :
curl -s https://install.crowdsec.net | sudo sh
sudo apt install crowdsec-firewall-bouncer-iptables
docker exec crowdsec cscli bouncers add firewall-bouncerPuis renseigner la clé API générée dans /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml (api_url et api_key), et redémarrer le service :
sudo systemctl restart crowdsec-firewall-bouncerModule AppSec (WAF) — optionnel
CrowdSec propose également un module de type WAF (règles OWASP CRS) analysant directement le contenu des requêtes, en complément de l'analyse de logs :
# Dans l'environnement du conteneur crowdsec
COLLECTIONS=... crowdsecurity/appsec-generic-rules crowdsecurity/appsec-crs# dynamic/middlewares.yml, dans la config du bouncer
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: trueBonnes pratiques
- Ne jamais exposer le dashboard Traefik sans authentification (
insecure: trueuniquement en test local). - Toujours mettre
exposedByDefault: false: un conteneur ne doit être routé que s'il porte explicitementtraefik.enable=true. - Sauvegarder régulièrement
acme.json(contient les clés privées des certificats). - Utiliser le DNS Challenge dès que des certificats wildcard sont nécessaires ou que le port 80 ne peut pas être exposé publiquement.
- Combiner le middleware CrowdSec avec les access logs en
jsonpour un maximum de contexte lors de l'analyse des alertes. - Isoler CrowdSec du réseau public (pas de port exposé sauf en
127.0.0.1si besoin de debug via l'API).
