Aller au contenu

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.fr vers un serveur, cloud.domaine.fr vers 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 certResolver déclaré sur l'entrypoint websecure s'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: :

caServer: https://acme-staging-v02.api.letsencrypt.org/directory

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) :

apt install -y apache2-utils
htpasswd -nB admin

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 :

http:
  serversTransports:
    app-autosigne:
      insecureSkipVerify: true
  services:
    app:
      loadBalancer:
        serversTransport: app-autosigne
        servers:
          - url: "https://10.0.0.30:8443"
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 :

ssh -L 8080:127.0.0.1:8080 utilisateur@serveur-traefik

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 :

docker network create proxy
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
  • @file indique 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 :

filenames:
  - /var/log/traefik/access.log
labels:
  type: traefik
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

Références