◀ Retour au blog
Docker

Traefik et Docker : reverse proxy et HTTPS automatique

Publié le 25 Aug 2026· 5 min de lecture
#Docker#Traefik#HTTPS#Reverse proxy

Pourquoi Traefik ?

Dès qu'un serveur héberge plusieurs applications conteneurisées, il faut un reverse proxy devant elles : un seul point d'entrée sur les ports 80 et 443, qui route chaque domaine vers le bon conteneur et gère le HTTPS. Nginx sait très bien le faire, mais chaque nouvelle application demande d'écrire un virtual host, de recharger la configuration et de générer un certificat.

Traefik inverse la logique : il lit l'API Docker, découvre les conteneurs, et les configure à partir de leurs labels. Démarrer un conteneur suffit pour le publier en HTTPS, avec un certificat Let's Encrypt obtenu et renouvelé automatiquement.

Le réseau partagé

Traefik et les applications qu'il expose doivent partager un réseau Docker. Créez-le une fois pour toutes :

docker network create proxy

Traefik lui-même

Un fichier /opt/traefik/compose.yaml :

services:
  traefik:
    image: traefik:v3.5
    restart: unless-stopped
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.docker.network=proxy
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - [email protected]
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
      - --api.dashboard=true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=le
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$Hq3vN2kS$$exempleDeHashAremplacer

networks:
  proxy:
    external: true

Quelques points importants :

  • exposedbydefault=false : aucun conteneur n'est publié sans traefik.enable=true. Sans cette option, une base de données démarrée par erreur pourrait se retrouver exposée.
  • La redirection HTTP → HTTPS est déclarée une fois, au niveau de l'entrypoint web.
  • Le socket Docker est monté en lecture seule. Il donne tout de même beaucoup de pouvoir sur l'hôte : pour un durcissement supplémentaire, placez un proxy de socket (comme tecnativa/docker-socket-proxy) entre Traefik et Docker.
  • Dans un fichier Compose, les $ du hash doivent être doublés ($$). Générez le hash avec htpasswd -nb admin motdepasse (paquet apache2-utils).
cd /opt/traefik && docker compose up -d
docker compose logs -f traefik

Publier une application

Côté application, il suffit de rejoindre le réseau proxy et de décrire le routage en labels. Exemple avec un conteneur Symfony (PHP-FPM + Nginx dans l'image, sur le port 8080) :

services:
  app:
    image: ghcr.io/moi/app:latest
    restart: unless-stopped
    env_file: .env.prod
    networks:
      - proxy
      - internal
    labels:
      - traefik.enable=true
      - traefik.http.routers.app.rule=Host(`app.example.com`) || Host(`www.app.example.com`)
      - traefik.http.routers.app.entrypoints=websecure
      - traefik.http.routers.app.tls.certresolver=le
      - traefik.http.routers.app.middlewares=secure-headers,rate-limit
      - traefik.http.services.app.loadbalancer.server.port=8080
      - traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000
      - traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true
      - traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true
      - traefik.http.middlewares.secure-headers.headers.frameDeny=true
      - traefik.http.middlewares.rate-limit.ratelimit.average=50
      - traefik.http.middlewares.rate-limit.ratelimit.burst=100

  database:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: app
      MYSQL_USER: app
      MYSQL_PASSWORD_FILE: /run/secrets/db_password
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
    secrets:
      - db_password
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - internal

networks:
  proxy:
    external: true
  internal:

volumes:
  db-data:

secrets:
  db_password:
    file: ./secrets/db_password.txt

La base de données n'est reliée qu'au réseau internal : Traefik ne peut pas l'atteindre, et elle n'a aucun port publié. Au premier docker compose up -d, Traefik détecte le conteneur, obtient le certificat pour les deux domaines et commence à router le trafic, en quelques secondes et sans aucun redémarrage.

Lire un label de routage

Les labels suivent tous la même grammaire : traefik.http.<type>.<nom>.<option>.

  • routers : quelle requête (règle Host, PathPrefix…) arrive sur quel entrypoint, avec quel TLS et quels middlewares
  • services : vers quel port du conteneur envoyer le trafic (indispensable si l'image expose plusieurs ports)
  • middlewares : les transformations appliquées en chemin (en-têtes, authentification, limitation de débit, redirections, compression)

Un middleware déclaré sur un conteneur peut être réutilisé par les autres : définissez vos en-têtes de sécurité une fois, sur le conteneur Traefik, puis référencez-les avec le suffixe @docker, par exemple secure-headers@docker.

Certificats wildcard avec le challenge DNS

Le challenge HTTP nécessite que le serveur soit joignable sur le port 80. Pour un certificat *.example.com, ou un serveur non exposé, utilisez le challenge DNS. Avec Cloudflare par exemple :

      - --certificatesresolvers.le.acme.dnschallenge=true
      - --certificatesresolvers.le.acme.dnschallenge.provider=cloudflare
    environment:
      CF_DNS_API_TOKEN_FILE: /run/secrets/cf_token

Le jeton Cloudflare n'a besoin que de la permission Zone → DNS → Edit sur la zone concernée.

Dépannage

  • Erreur 404 « page not found » : le routeur n'existe pas. Vérifiez traefik.enable=true et la règle Host (les backticks sont obligatoires).
  • Erreur 502 Bad Gateway : Traefik trouve le conteneur mais ne le joint pas. Le conteneur est-il sur le réseau proxy ? Le port du loadbalancer est-il le bon ?
  • Certificat auto-signé « TRAEFIK DEFAULT CERT » : le challenge ACME a échoué. Consultez les logs de Traefik ; le DNS pointe-t-il vers le serveur, et le port 80 est-il ouvert ?
  • Limite de Let's Encrypt : pendant vos essais, utilisez le serveur de test (--certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory) pour ne pas atteindre les quotas.

Une fois en place, ajouter une application revient à écrire trois ou quatre labels. C'est ce qui rend Traefik si agréable pour un serveur personnel ou une petite infrastructure qui héberge beaucoup de services.