[{"data":1,"prerenderedAt":6},["ShallowReactive",2],{"article:fr:traefik-docker-https":3},{"html":4,"lang":5},"\u003Ch2>Pourquoi Traefik ?\u003C\u002Fh2>\n\u003Cp>Dès qu'un serveur héberge plusieurs applications conteneurisées, il faut un \u003Cstrong>reverse proxy\u003C\u002Fstrong> 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.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Traefik\u003C\u002Fstrong> inverse la logique : il lit l'API Docker, découvre les conteneurs, et les configure à partir de leurs \u003Cstrong>labels\u003C\u002Fstrong>. Démarrer un conteneur suffit pour le publier en HTTPS, avec un certificat Let's Encrypt obtenu et renouvelé automatiquement.\u003C\u002Fp>\n\n\u003Ch3>Le réseau partagé\u003C\u002Fh3>\n\u003Cp>Traefik et les applications qu'il expose doivent partager un réseau Docker. Créez-le une fois pour toutes :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>docker network create proxy\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Traefik lui-même\u003C\u002Fh3>\n\u003Cp>Un fichier \u003Ccode>\u002Fopt\u002Ftraefik\u002Fcompose.yaml\u003C\u002Fcode> :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>services:\n  traefik:\n    image: traefik:v3.5\n    restart: unless-stopped\n    command:\n      - --providers.docker=true\n      - --providers.docker.exposedbydefault=false\n      - --providers.docker.network=proxy\n      - --entrypoints.web.address=:80\n      - --entrypoints.web.http.redirections.entrypoint.to=websecure\n      - --entrypoints.web.http.redirections.entrypoint.scheme=https\n      - --entrypoints.websecure.address=:443\n      - --certificatesresolvers.le.acme.email=contact@example.com\n      - --certificatesresolvers.le.acme.storage=\u002Fletsencrypt\u002Facme.json\n      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web\n      - --api.dashboard=true\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    volumes:\n      - \u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock:ro\n      - .\u002Fletsencrypt:\u002Fletsencrypt\n    networks:\n      - proxy\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)\n      - traefik.http.routers.dashboard.entrypoints=websecure\n      - traefik.http.routers.dashboard.tls.certresolver=le\n      - traefik.http.routers.dashboard.service=api@internal\n      - traefik.http.routers.dashboard.middlewares=dashboard-auth\n      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$Hq3vN2kS$$exempleDeHashAremplacer\n\nnetworks:\n  proxy:\n    external: true\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Quelques points importants :\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ccode>exposedbydefault=false\u003C\u002Fcode>\u003C\u002Fstrong> : aucun conteneur n'est publié sans \u003Ccode>traefik.enable=true\u003C\u002Fcode>. Sans cette option, une base de données démarrée par erreur pourrait se retrouver exposée.\u003C\u002Fli>\n\u003Cli>La \u003Cstrong>redirection HTTP → HTTPS\u003C\u002Fstrong> est déclarée une fois, au niveau de l'entrypoint \u003Ccode>web\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Le \u003Cstrong>socket Docker\u003C\u002Fstrong> 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 \u003Ccode>tecnativa\u002Fdocker-socket-proxy\u003C\u002Fcode>) entre Traefik et Docker.\u003C\u002Fli>\n\u003Cli>Dans un fichier Compose, les \u003Ccode>$\u003C\u002Fcode> du hash doivent être doublés (\u003Ccode>$$\u003C\u002Fcode>). Générez le hash avec \u003Ccode>htpasswd -nb admin motdepasse\u003C\u002Fcode> (paquet \u003Ccode>apache2-utils\u003C\u002Fcode>).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cpre>\u003Ccode>cd \u002Fopt\u002Ftraefik &amp;&amp; docker compose up -d\ndocker compose logs -f traefik\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>Publier une application\u003C\u002Fh3>\n\u003Cp>Côté application, il suffit de rejoindre le réseau \u003Ccode>proxy\u003C\u002Fcode> et de décrire le routage en labels. Exemple avec un conteneur Symfony (PHP-FPM + Nginx dans l'image, sur le port 8080) :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>services:\n  app:\n    image: ghcr.io\u002Fmoi\u002Fapp:latest\n    restart: unless-stopped\n    env_file: .env.prod\n    networks:\n      - proxy\n      - internal\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.app.rule=Host(`app.example.com`) || Host(`www.app.example.com`)\n      - traefik.http.routers.app.entrypoints=websecure\n      - traefik.http.routers.app.tls.certresolver=le\n      - traefik.http.routers.app.middlewares=secure-headers,rate-limit\n      - traefik.http.services.app.loadbalancer.server.port=8080\n      - traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000\n      - traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true\n      - traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true\n      - traefik.http.middlewares.secure-headers.headers.frameDeny=true\n      - traefik.http.middlewares.rate-limit.ratelimit.average=50\n      - traefik.http.middlewares.rate-limit.ratelimit.burst=100\n\n  database:\n    image: mysql:8.4\n    restart: unless-stopped\n    environment:\n      MYSQL_DATABASE: app\n      MYSQL_USER: app\n      MYSQL_PASSWORD_FILE: \u002Frun\u002Fsecrets\u002Fdb_password\n      MYSQL_RANDOM_ROOT_PASSWORD: \"yes\"\n    secrets:\n      - db_password\n    volumes:\n      - db-data:\u002Fvar\u002Flib\u002Fmysql\n    networks:\n      - internal\n\nnetworks:\n  proxy:\n    external: true\n  internal:\n\nvolumes:\n  db-data:\n\nsecrets:\n  db_password:\n    file: .\u002Fsecrets\u002Fdb_password.txt\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>La base de données n'est reliée qu'au réseau \u003Ccode>internal\u003C\u002Fcode> : Traefik ne peut pas l'atteindre, et elle n'a aucun port publié. Au premier \u003Ccode>docker compose up -d\u003C\u002Fcode>, 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.\u003C\u002Fp>\n\n\u003Ch3>Lire un label de routage\u003C\u002Fh3>\n\u003Cp>Les labels suivent tous la même grammaire : \u003Ccode>traefik.http.&lt;type&gt;.&lt;nom&gt;.&lt;option&gt;\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>routers\u003C\u002Fstrong> : quelle requête (règle \u003Ccode>Host\u003C\u002Fcode>, \u003Ccode>PathPrefix\u003C\u002Fcode>…) arrive sur quel entrypoint, avec quel TLS et quels middlewares\u003C\u002Fli>\n\u003Cli>\u003Cstrong>services\u003C\u002Fstrong> : vers quel port du conteneur envoyer le trafic (indispensable si l'image expose plusieurs ports)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>middlewares\u003C\u002Fstrong> : les transformations appliquées en chemin (en-têtes, authentification, limitation de débit, redirections, compression)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>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 \u003Ccode>@docker\u003C\u002Fcode>, par exemple \u003Ccode>secure-headers@docker\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch3>Certificats wildcard avec le challenge DNS\u003C\u002Fh3>\n\u003Cp>Le challenge HTTP nécessite que le serveur soit joignable sur le port 80. Pour un certificat \u003Ccode>*.example.com\u003C\u002Fcode>, ou un serveur non exposé, utilisez le challenge DNS. Avec Cloudflare par exemple :\u003C\u002Fp>\n\u003Cpre>\u003Ccode>      - --certificatesresolvers.le.acme.dnschallenge=true\n      - --certificatesresolvers.le.acme.dnschallenge.provider=cloudflare\n    environment:\n      CF_DNS_API_TOKEN_FILE: \u002Frun\u002Fsecrets\u002Fcf_token\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Le jeton Cloudflare n'a besoin que de la permission \u003Cem>Zone → DNS → Edit\u003C\u002Fem> sur la zone concernée.\u003C\u002Fp>\n\n\u003Ch3>Dépannage\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>Erreur 404 « page not found »\u003C\u002Fstrong> : le routeur n'existe pas. Vérifiez \u003Ccode>traefik.enable=true\u003C\u002Fcode> et la règle \u003Ccode>Host\u003C\u002Fcode> (les backticks sont obligatoires).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Erreur 502 Bad Gateway\u003C\u002Fstrong> : Traefik trouve le conteneur mais ne le joint pas. Le conteneur est-il sur le réseau \u003Ccode>proxy\u003C\u002Fcode> ? Le port du \u003Ccode>loadbalancer\u003C\u002Fcode> est-il le bon ?\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Certificat auto-signé « TRAEFIK DEFAULT CERT »\u003C\u002Fstrong> : 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 ?\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Limite de Let's Encrypt\u003C\u002Fstrong> : pendant vos essais, utilisez le serveur de test (\u003Ccode>--certificatesresolvers.le.acme.caserver=https:\u002F\u002Facme-staging-v02.api.letsencrypt.org\u002Fdirectory\u003C\u002Fcode>) pour ne pas atteindre les quotas.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>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.\u003C\u002Fp>\n","fr",1790543096504]