Traefik
Mise à jour : 2026-10-19 | Relu intégralement : 2026-10-19
Traefik est un reverse proxy moderne. Il reçoit toutes les requêtes web arrivant sur votre serveur et les transmet au bon service interne, en fonction du nom de domaine demandé. Il gère aussi automatiquement les certificats HTTPS avec Let's Encrypt.
Il joue trois rôles :
- aiguilleur : il dirige
status.domaine.frvers un serveur,cloud.domaine.frvers un autre; - gestionnaire de certificats : il obtient et renouvelle seul les certificats TLS, sans aucune intervention manuelle;
- point d'entrée unique : un seul serveur exposé sur Internet (ports 80 et 443) protège tous les services placés derrière lui, qui n'ont plus besoin d'adresse publique.
Versions visées
- Traefik 3.7
- Docker Engine avec Compose v2 (commande
docker compose) - Debian 12 ou 13
Architecture type
```text
Internet
│
Ports 80 / 443 uniquement
│
▼
+---------------+
| Traefik | serveur proxy (seul exposé)
+---------------+
│ réseau privé
┌────────────────┼────────────────┐
▼ ▼ ▼
status.domaine.fr cloud.domaine.fr app.domaine.fr 10.0.0.20:3001 10.0.0.21:80 10.0.0.22:8069 ```
Les services tournent sur d'autres machines (ou VM) d'un réseau privé, sans adresse publique. Seul le serveur Traefik est joignable depuis Internet. Toute nouvelle exposition passe par lui : on ajoute un fichier de configuration, et le service est en ligne, en HTTPS.
Comprendre le routage
Analogie : Traefik fonctionne comme une gare de triage.
- Les entrypoints sont les portes d'entrée de la gare : le port 80 (HTTP) et le port 443 (HTTPS).
- Les routers sont les aiguillages : ils regardent chaque requête (quel nom de domaine ? quel chemin ?) et choisissent la voie.
- Les middlewares sont les contrôles placés sur la voie : redirection, authentification, limitation de débit, en-têtes de sécurité...
- Les services sont les destinations finales : l'adresse interne du serveur qui répond.
Requête : GET https://status.domaine.fr
1. Entrypoint websecure (:443) reçoit la requête, déchiffre le TLS
2. Router "status" la règle Host(`status.domaine.fr`) correspond
3. Middlewares en-têtes de sécurité, limitation de débit...
4. Service "status" transmet à http://10.0.0.20:3001
5. Réponse renvoyée au navigateur, chiffrée par Traefik
Les règles des routers
# Un nom de domaine
rule: "Host(`app.domaine.fr`)"
# Un domaine ET un chemin
rule: "Host(`app.domaine.fr`) && PathPrefix(`/api`)"
# Plusieurs domaines
rule: "Host(`domaine.fr`) || Host(`www.domaine.fr`)"
Les noms de domaine s'écrivent entre accents graves (`), pas entre apostrophes.
Configuration statique et dynamique
C'est le principe central de Traefik.
| Configuration statique | Configuration dynamique | |
|---|---|---|
| Contenu | L'infrastructure : ports d'écoute, certificats, sources de configuration | Les routes : routers, middlewares, services |
| Fichier | traefik.yml |
Un fichier par service dans dynamic/ |
| Prise en compte | Au redémarrage de Traefik | À chaud, dès l'enregistrement du fichier |
Ajouter, modifier ou retirer un service ne demande donc jamais de redémarrer Traefik, et ne touche pas aux autres services.
Prérequis
- Un serveur Linux à jour avec Docker Engine et Compose v2.
- Les enregistrements DNS de chaque service (ex.
status.domaine.fr) pointant vers l'adresse publique de ce serveur. - Les ports 80 et 443 ouverts depuis Internet. Le port 80 reste indispensable même si tout est redirigé en HTTPS : Let's Encrypt l'utilise pour vérifier que vous contrôlez le domaine.
- Un accès d'administration hors Internet : VPN (WireGuard, NetBird...) ou tunnel SSH.
Installation
1. Créer la structure
En root :
mkdir -p /opt/traefik/dynamic /var/log/traefik
cd /opt/traefik
touch acme.json && chmod 600 acme.json
/opt/traefik/
├── compose.yml le conteneur Traefik
├── traefik.yml configuration statique
├── acme.json certificats Let's Encrypt (créé vide, rempli par Traefik)
└── dynamic/ configuration dynamique, un fichier par service
├── middlewares.yml
└── status.yml
acme.json contient la clé de votre compte Let's Encrypt et les clés privées des certificats : il doit rester en 600. S'il n'existe pas au démarrage, Docker crée un dossier à sa place et Traefik ne peut pas enregistrer les certificats.
2. Le fichier compose.yml
services:
traefik:
image: traefik:v3.7
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80" # HTTP : challenge Let's Encrypt et redirection
- "443:443" # HTTPS
- "127.0.0.1:8080:8080" # Dashboard : local uniquement (voir plus bas)
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./acme.json:/etc/traefik/acme.json
- /var/log/traefik:/var/log/traefik
L'image est épinglée sur v3.7 plutôt que latest : vous recevez les correctifs de la branche 3.7, mais une montée de version reste un acte volontaire et testé.
3. La configuration statique traefik.yml
Remplacez admin@domaine.fr par l'adresse qui recevra les avertissements de Let's Encrypt.
global:
checkNewVersion: false # pas de vérification de version au démarrage
sendAnonymousUsage: false # pas de télémétrie
log:
level: INFO
# Journaux d'accès en JSON, lisibles par CrowdSec
accessLog:
filePath: /var/log/traefik/access.log
format: json
# Dashboard sans authentification : son accès n'est restreint QUE par le réseau (voir plus bas)
api:
dashboard: true
insecure: true
entryPoints:
web:
address: ":80"
http:
# Redirection globale de tout le HTTP vers HTTPS
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
# Tous les routers de cet entrypoint obtiennent un certificat Let's Encrypt
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: admin@domaine.fr
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: web
providers:
file:
directory: /etc/traefik/dynamic
watch: true # rechargement à chaud des fichiers dynamiques
- La redirection globale sur l'entrypoint
webévite de répéter une redirection dans chaque fichier de service. Le challenge Let's Encrypt, lui, continue de fonctionner sur le port 80. - Le
certResolverdéclaré sur l'entrypointwebsecures'applique à tous les routers : chaque nouveau service obtient automatiquement son certificat.
Testez d'abord avec l'environnement de test de Let's Encrypt
Let's Encrypt limite le nombre de certificats délivrés par semaine. Pendant la mise au point, ajoutez cette ligne sous acme: :
Les certificats obtenus ne sont pas reconnus par les navigateurs, mais vous pouvez faire autant d'essais que nécessaire. Une fois tout validé, retirez la ligne, videz le fichier (: > acme.json) et redémarrez Traefik.
4. Les middlewares partagés
Créez dynamic/middlewares.yml. Définissez chaque middleware une seule fois, dans ce fichier, puis utilisez-le dans autant de services que nécessaire : tous les fichiers du dossier dynamic/ sont fusionnés en une seule configuration, et un même nom défini dans plusieurs fichiers crée des conflits.
http:
middlewares:
# En-têtes de sécurité
secure-headers:
headers:
stsSeconds: 31536000 # HSTS : HTTPS obligatoire pendant un an
contentTypeNosniff: true
referrerPolicy: "strict-origin-when-cross-origin"
# Limitation de débit par adresse IP (anti brute-force)
rate-limit:
rateLimit:
average: 100 # requêtes par seconde en moyenne
burst: 50 # pic toléré
# Accès réservé au réseau d'administration
admin-only:
ipAllowList:
sourceRange:
- "100.64.0.0/10" # remplacez par la plage de votre VPN
Protéger un service par mot de passe
Pour un service sans authentification propre, le middleware basicAuth demande un identifiant et un mot de passe. Générez l'empreinte du mot de passe (le paquet apache2-utils fournit htpasswd) :
La commande demande le mot de passe sans l'afficher ni l'enregistrer dans l'historique du shell. Ajoutez le résultat dans middlewares.yml :
auth-admin:
basicAuth:
users:
- "admin:<EMPREINTE_HTPASSWD>" # le $2y$... renvoyé par htpasswd, collé tel quel
Dans un fichier YAML, le caractère $ s'écrit tel quel. Ce n'est que dans les labels Docker qu'il faut le doubler ($$).
5. Un premier service
Exemple : exposer status.domaine.fr, une application qui écoute sur 10.0.0.20:3001 dans le réseau privé. Créez dynamic/status.yml :
http:
routers:
status:
rule: "Host(`status.domaine.fr`)"
entryPoints:
- websecure
middlewares:
- secure-headers
service: status
services:
status:
loadBalancer:
servers:
- url: "http://10.0.0.20:3001"
Le terme loadBalancer peut surprendre pour un seul serveur : c'est simplement la syntaxe de Traefik pour déclarer une destination. En ajoutant d'autres lignes url, Traefik répartirait la charge entre plusieurs serveurs.
6. Démarrer et vérifier
cd /opt/traefik
docker compose config -q # valide compose.yml, rien ne doit s'afficher
docker compose up -d
docker compose logs -f traefik
Les journaux ne doivent afficher aucune erreur. Le premier certificat arrive en quelques secondes. Puis, depuis votre poste :
curl -sI http://status.domaine.fr | head -3 # attendu : redirection 3xx vers https
curl -sI https://status.domaine.fr | head -1 # attendu : HTTP/2 200 (ou 30x selon l'application)
Ajouter un nouveau service
C'est l'opération courante, et elle ne demande aucun redémarrage.
1) Créez l'enregistrement DNS du nouveau domaine vers le serveur Traefik. 2) Copiez un fichier existant :
```sh
cp /opt/traefik/dynamic/status.yml /opt/traefik/dynamic/monservice.yml
```
3) Dans la copie, adaptez trois choses : le nom du router et du service (ils doivent être uniques dans tout le dossier), la règle Host(), et l'url interne.
4) Enregistrez : Traefik détecte le fichier et l'applique immédiatement. Vérifiez docker compose logs --since 1m traefik, puis testez avec curl.
Pour retirer un service, supprimez simplement son fichier.
Application interne en HTTPS avec un certificat auto-signé
Certaines applications n'écoutent qu'en HTTPS avec leur propre certificat auto-signé. Traefik refuse par défaut de s'y connecter. Déclarez un transport qui ignore la vérification, uniquement pour ce service et sur un réseau privé de confiance :
Retrouver la vraie adresse des visiteurs
Derrière Traefik, l'application voit toutes les requêtes arriver depuis l'adresse de Traefik. Traefik transmet l'adresse réelle du visiteur dans l'en-tête X-Forwarded-For, mais l'application doit être configurée pour faire confiance à cet en-tête venant de Traefik. Le réglage s'appelle souvent "proxy de confiance" ou trusted_proxies. Sans lui, les journaux de l'application, ses alertes et ses protections anti brute-force ne voient qu'une seule adresse : celle du proxy.
Le dashboard
Le dashboard affiche en temps réel les routers, services et middlewares chargés, avec leurs erreurs éventuelles. C'est le premier endroit à consulter quand un service ne répond pas.
Il est configuré sans authentification (insecure: true) : il ne doit jamais être joignable depuis Internet. Toute la protection repose sur une seule chose : le port n'est publié que sur 127.0.0.1 dans le compose.yml ci-dessus. Ne le publiez jamais sur 0.0.0.0 (rappel : Docker contournerait votre pare-feu, voir l'encadré plus bas), et si vous voulez une défense en profondeur, appliquez le middleware admin-only au router du dashboard plutôt que de compter sur le seul 127.0.0.1. Pour y accéder depuis votre poste, ouvrez un tunnel SSH :
Puis ouvrez http://localhost:8080 dans votre navigateur.
Si vous avez un VPN d'administration, vous pouvez aussi publier le port sur l'adresse VPN du serveur ("100.x.y.z:8080:8080").
Docker contourne le pare-feu de l'hôte
Un port publié par Docker sur toutes les interfaces ("8080:8080") est ouvert même si votre pare-feu local (UFW...) le bloque : Docker insère ses propres règles avant celles du pare-feu. Ne comptez jamais sur UFW pour masquer un port publié par Docker. Limitez l'adresse d'écoute dans ports:, ou filtrez en amont (pare-feu de l'hébergeur, de l'hyperviseur).
Utiliser les labels Docker
Quand les applications tournent dans des conteneurs sur le même serveur que Traefik, on peut décrire les routes directement dans leurs labels Docker, plutôt que dans des fichiers. Les deux méthodes peuvent cohabiter.
Ajoutez le fournisseur Docker dans traefik.yml :
providers:
file:
directory: /etc/traefik/dynamic
watch: true
docker:
exposedByDefault: false # rien n'est publié sans traefik.enable=true
network: proxy # réseau Docker partagé avec les applications
Créez le réseau partagé, puis dans le compose.yml de Traefik, ajoutez le socket Docker en lecture seule et le réseau :
services:
traefik:
# ... (reste inchangé)
volumes:
# ... (volumes existants)
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
networks:
proxy:
external: true
Chaque application déclare ensuite ses routes dans ses labels, et rejoint le réseau proxy :
services:
monapp:
image: monimage:1.0
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.monapp.rule=Host(`app.domaine.fr`)"
- "traefik.http.routers.monapp.entrypoints=websecure"
- "traefik.http.routers.monapp.middlewares=secure-headers@file"
- "traefik.http.services.monapp.loadbalancer.server.port=8000"
networks:
proxy:
external: true
@fileindique que le middleware vient du fournisseur fichier (middlewares.yml). Depuis les labels, ce suffixe est obligatoire.- Le conteneur n'a pas besoin de publier de port (
ports:) : Traefik le joint par le réseau Docker.
Le socket Docker est un accès sensible
Même en lecture seule, le socket Docker permet de tout savoir sur les conteneurs de la machine, et une faille dans Traefik pourrait l'exploiter. Pour aller plus loin, placez devant un proxy de socket Docker, qui n'autorise que les requêtes de lecture dont Traefik a besoin. Si toutes vos applications sont sur d'autres machines, n'activez pas ce fournisseur : le fournisseur fichier suffit.
Journaux et CrowdSec
Les journaux d'accès au format JSON, dans /var/log/traefik/access.log, permettent à CrowdSec de détecter les attaques sur tous les services publiés. Déclarez-les dans /etc/crowdsec/acquis.d/traefik.yaml :
cscli collections install crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios
systemctl restart crowdsec
Ajoutez une rotation pour que le fichier ne grossisse pas indéfiniment, dans /etc/logrotate.d/traefik :
/var/log/traefik/access.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
postrotate
docker kill --signal="USR1" traefik >/dev/null 2>&1 || true
endscript
}
Le signal USR1 demande à Traefik de rouvrir son fichier de journal après la rotation.
Sauvegarde
Tout tient dans /opt/traefik/ : configuration statique, fichiers dynamiques et certificats. Sauvegardez ce dossier entier, en le traitant comme un secret : acme.json contient des clés privées. Si vous le perdez, Traefik redemande simplement de nouveaux certificats au démarrage.
Mise à jour
1) Lisez les notes de version de Traefik : la configuration peut changer d'une version à l'autre.
2) Assurez-vous d'avoir une sauvegarde ou un snapshot récent du serveur.
3) Pour une nouvelle branche, modifiez le tag dans compose.yml (ex. traefik:v3.7 vers la branche suivante). Pour les simples correctifs de la branche actuelle, ne changez rien.
4) Appliquez :
```sh
cd /opt/traefik
docker compose pull
docker compose up -d
```
5) Vérifiez les journaux (docker compose logs -f traefik), le dashboard, et testez un service avec curl.
Diagnostic
| Symptôme | Cause probable | Que vérifier |
|---|---|---|
404 page not found |
Aucun router ne correspond à la requête | La règle Host(), une erreur de syntaxe dans le fichier dynamique (journaux et dashboard) |
502 Bad Gateway |
Traefik n'atteint pas l'application | L'url du service, que l'application tourne, le pare-feu de la machine interne |
504 Gateway Timeout |
L'application ne répond pas à temps | La charge de l'application, le réseau entre Traefik et elle |
Certificat TRAEFIK DEFAULT CERT |
Let's Encrypt n'a pas délivré le certificat | Le DNS du domaine, le port 80 ouvert, les erreurs acme dans les journaux |
middleware ... does not exist |
Middleware mal nommé ou absent | Son nom dans middlewares.yml, le suffixe @file dans les labels |
# Journaux en direct
docker compose logs -f traefik
# Erreurs de la dernière minute
docker compose logs --since 1m traefik
# Redémarrer (nécessaire seulement après une modification de traefik.yml)
docker compose restart traefik