devops x securite x traefik

Proteger un Repertoire avec BasicAuth sur Traefik

Ajouter une authentification par mot de passe sur un chemin specifique tout en laissant le reste du site public.

janvier 2026 . 12 min de lecture

Vous avez un site web public servi par Traefik, mais vous souhaitez proteger uniquement certains repertoires sensibles avec un mot de passe ? Ce tutoriel vous guide pas a pas pour configurer BasicAuth sur un chemin specifique, tout en gardant le reste du site accessible publiquement.


1. Contexte et Prerequis

situation de depart

Nous avons un site web statique servi par nginx derriere Traefik (reverse proxy). Le site est accessible publiquement sur https://ops-imperium.com.

Nous voulons proteger uniquement le repertoire /formations/ avec un mot de passe, tout en laissant le reste du site accessible publiquement.

prerequis

stack technique

composant version role
Traefik v3.2 Reverse proxy + SSL automatique
nginx alpine Serveur web statique
Docker Compose v2+ Orchestration

2. Architecture Actuelle

structure des fichiers sur le serveur

/opt/ops-imperium/
+-- docker-compose.yml    # Configuration Docker
+-- acme.json             # Certificats Let's Encrypt
+-- html/                 # Fichiers statiques
    +-- index.html
    +-- blog/
    +-- formations/       # <-- Repertoire a proteger
        +-- index.html
        +-- 0801-iac-generation/
        +-- 0802-iac-context/
        +-- ...

configuration traefik actuelle

Voici le docker-compose.yml initial (sans protection):

services:
  traefik:
    image: traefik:v3.2
    container_name: traefik
    restart: always
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=your@email.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./acme.json:/acme.json"
    networks:
      - traefik-public

  website:
    image: nginx:alpine
    container_name: ops-imperium-web
    restart: always
    volumes:
      - ./html:/usr/share/nginx/html:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.website.rule=Host(`ops-imperium.com`)"
      - "traefik.http.routers.website.entrypoints=websecure"
      - "traefik.http.routers.website.tls.certresolver=letsencrypt"
      - "traefik.http.services.website.loadbalancer.server.port=80"
    networks:
      - traefik-public

networks:
  traefik-public:
    external: true

Cette configuration route TOUT le trafic vers nginx sans authentification.

architecture traefik avec basicauth
https
traefik
formations + basicauth
PathPrefix(/formations)
p:100
website
Host(ops-imperium.com)
p:10
nginx

3. Generer le Hash du Mot de Passe

pourquoi hasher le mot de passe ?

Traefik utilise le format htpasswd pour stocker les credentials. Le mot de passe n'est jamais stocke en clair - on utilise un hash bcrypt.

etape 3.1 : installer htpasswd (si necessaire)

Etape 3.1
# Sur Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y apache2-utils

# Sur Alpine (dans un container)
apk add --no-cache apache2-utils

etape 3.2 : generer le hash

Etape 3.2

Commande:

htpasswd -nB team

Resultat:

team:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Animation terminal montrant la generation du hash bcrypt avec htpasswd
Generation du hash bcrypt avec htpasswd

etape 3.3 : echapper les $ pour docker compose

Etape 3.3

important

Dans un fichier docker-compose.yml, le caractere $ est interprete comme une variable d'environnement. Il faut doubler chaque $.

# Commande qui echappe automatiquement
echo $(htpasswd -nB team aidd2026forthewin) | sed -e 's/\$/\$\$/g'

Resultat echappe (exemple):

team:$$2y$$05$$xxxx...

Astuce: Gardez le hash non-echappe dans un gestionnaire de mots de passe, et l'echappe dans votre docker-compose.


4. Modifier la Configuration Traefik

concept : deux routers, un service

Traefik permet de definir plusieurs routers pour un meme service. On va creer:

  1. Router website: Pour tout le site (sans auth)
  2. Router formations: Pour /formations/ uniquement (avec BasicAuth)
avant / apres : protection par chemin
avant
Tout le trafic /*
1 router public website
pas de protection
apres
BasicAuth /formations/*
Public /*
2 routers avec priorites

etape 4.1 : sauvegarder la configuration actuelle

Etape 4.1
ssh ops-imperium "cp /opt/ops-imperium/docker-compose.yml /opt/ops-imperium/docker-compose.yml.backup"

etape 4.2 : ajouter les labels basicauth

Etape 4.2

Ajoutez ces labels au service website dans docker-compose.yml:

labels:
  # === ROUTER PRINCIPAL (tout le site, sans auth) ===
  - "traefik.enable=true"
  - "traefik.http.routers.website.rule=Host(`ops-imperium.com`) || Host(`www.ops-imperium.com`)"
  - "traefik.http.routers.website.entrypoints=websecure"
  - "traefik.http.routers.website.tls.certresolver=letsencrypt"
  - "traefik.http.services.website.loadbalancer.server.port=80"
  - "traefik.http.routers.website.middlewares=security-headers"

  # === ROUTER FORMATIONS (avec BasicAuth) ===
  - "traefik.http.routers.formations.rule=Host(`ops-imperium.com`) && PathPrefix(`/formations`)"
  - "traefik.http.routers.formations.entrypoints=websecure"
  - "traefik.http.routers.formations.tls.certresolver=letsencrypt"
  - "traefik.http.routers.formations.service=website"
  - "traefik.http.routers.formations.middlewares=formations-auth,security-headers"

  # === MIDDLEWARE BASICAUTH ===
  - "traefik.http.middlewares.formations-auth.basicauth.users=team:$$2y$$05$$VOTRE_HASH_ICI"

Points cles:

important : priorite des routeurs

Par defaut, Traefik calcule la priorite des routeurs basee sur la longueur de la regle. Cependant, avec des regles utilisant || (OR), ce calcul peut etre imprevisible. Solution: Definir des priorites explicites (voir section 4.3).

etape 4.3 : configuration complete

Etape 4.3

Voici le docker-compose.yml complet avec BasicAuth:

services:
  # --- TRAEFIK v3 (Reverse Proxy + SSL automatique) ---
  traefik:
    image: traefik:v3.2
    container_name: traefik
    restart: always
    command:
      - "--api.insecure=false"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik-public"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=victor@ops-imperium.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme.json"
      - "--log.level=INFO"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./acme.json:/acme.json"
    networks:
      - traefik-public

  # --- SITE WEB (Nginx) ---
  website:
    image: nginx:alpine
    container_name: ops-imperium-web
    restart: always
    volumes:
      - ./html:/usr/share/nginx/html:ro
    labels:
      - "traefik.enable=true"

      # === ROUTER PRINCIPAL (tout le site) ===
      - "traefik.http.routers.website.rule=Host(`ops-imperium.com`) || Host(`www.ops-imperium.com`)"
      - "traefik.http.routers.website.entrypoints=websecure"
      - "traefik.http.routers.website.tls.certresolver=letsencrypt"
      - "traefik.http.services.website.loadbalancer.server.port=80"
      - "traefik.http.routers.website.middlewares=security-headers"
      # Priorite BASSE pour le routeur general
      - "traefik.http.routers.website.priority=10"

      # === ROUTER FORMATIONS (avec BasicAuth) ===
      - "traefik.http.routers.formations.rule=Host(`ops-imperium.com`) && PathPrefix(`/formations`)"
      - "traefik.http.routers.formations.entrypoints=websecure"
      - "traefik.http.routers.formations.tls.certresolver=letsencrypt"
      - "traefik.http.routers.formations.service=website"
      - "traefik.http.routers.formations.middlewares=formations-auth,security-headers"
      # Priorite HAUTE pour le routeur specifique (CRITIQUE!)
      - "traefik.http.routers.formations.priority=100"

      # === MIDDLEWARE BASICAUTH ===
      - "traefik.http.middlewares.formations-auth.basicauth.users=team:$$2y$$05$$HASH_A_REMPLACER"

      # === MIDDLEWARE SECURITY HEADERS ===
      - "traefik.http.middlewares.security-headers.headers.stsSeconds=31536000"
      - "traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
      - "traefik.http.middlewares.security-headers.headers.stsPreload=true"
      - "traefik.http.middlewares.security-headers.headers.frameDeny=true"
      - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.security-headers.headers.browserXssFilter=true"
      - "traefik.http.middlewares.security-headers.headers.referrerPolicy=strict-origin-when-cross-origin"
    networks:
      - traefik-public

networks:
  traefik-public:
    external: true

5. Deployer et Tester

etape 5.1 : deployer la nouvelle configuration

Etape 5.1
cd /opt/ops-imperium
docker-compose up -d
Animation terminal montrant l'arret et le redemarrage des containers Docker
Arret et redemarrage des containers Docker

Sortie attendue:

[+] Running 2/2
 Container traefik          Running
 Container ops-imperium-web Started

etape 5.2 : verifier les logs traefik

Etape 5.2
docker logs traefik --tail 50

Cherchez les lignes indiquant la creation des routers:

level=info msg="Adding route for formations..."
level=info msg="Adding route for website..."

etape 5.3 : tester l'acces

Etape 5.3

Test 1: Page publique (sans auth)

curl -I https://ops-imperium.com/

Devrait retourner 200 OK

Test 2: Page protegee (sans credentials)

curl -I https://ops-imperium.com/formations/

Devrait retourner 401 Unauthorized

Test 3: Page protegee (avec credentials)

curl -I -u team:aidd2026forthewin https://ops-imperium.com/formations/

Devrait retourner 200 OK

Animation terminal montrant les 3 tests curl de validation BasicAuth
Validation des 3 tests (200 -> 401 -> 200)

etape 5.4 : tester dans le navigateur

Etape 5.4
  1. Ouvrir https://ops-imperium.com/formations/
  2. Une popup d'authentification apparait
  3. Entrer team / aidd2026forthewin
  4. La page s'affiche
Capture d'ecran montrant la page index des formations apres authentification reussie
Page index des formations apres login reussi
Capture d'ecran montrant la page d'accueil publique sans authentification
Page d'accueil publique accessible sans authentification

6. Depannage

erreur : 404 not found sur /formations/

Cause: Le fichier index.html n'existe pas dans /formations/

Solution:

ls -la /opt/ops-imperium/html/formations/
# Verifier que index.html existe

erreur : 500 internal server error

Cause: Erreur de syntaxe dans les labels Docker

Solution:

# Verifier la syntaxe YAML
docker-compose config

# Regarder les logs Traefik
docker logs traefik --tail 100 | grep -i error

erreur : basicauth ne fonctionne pas (pas de popup)

Causes possibles:

  1. Le hash contient des $ non echappes
  2. Le middleware n'est pas attache au router
  3. Le routeur general capture le trafic avant le routeur protege (cause la plus frequente!)

Diagnostic:

# Test rapide : la page protegee retourne-t-elle 401 ?
curl -s -o /dev/null -w "%{http_code}" https://ops-imperium.com/formations/
# Si retourne 200 au lieu de 401 -> probleme de priorite

Solution: Ajouter des priorites explicites

# Routeur general = priorite BASSE
- "traefik.http.routers.website.priority=10"

# Routeur protege = priorite HAUTE
- "traefik.http.routers.formations.priority=100"

astuce

Plus le numero est eleve, plus la priorite est haute. Le routeur avec la plus haute priorite est evalue en premier.

le mot de passe ne fonctionne pas

Cause: Hash incorrect ou mal echappe

Solution: Regenerer le hash et verifier l'echappement des $


7. Pour Aller Plus Loin

ajouter plusieurs utilisateurs

- "traefik.http.middlewares.formations-auth.basicauth.users=user1:$$hash1,user2:$$hash2"

utiliser un fichier externe pour les credentials

- "traefik.http.middlewares.formations-auth.basicauth.usersfile=/path/to/.htpasswd"

personnaliser le message d'authentification

- "traefik.http.middlewares.formations-auth.basicauth.realm=Formations AIDD"

proteger plusieurs chemins

- "traefik.http.routers.protected.rule=Host(`example.com`) && (PathPrefix(`/admin`) || PathPrefix(`/api`))"

retirer la protection (temporairement)

Commenter les labels du router formations et redeployer:

docker-compose up -d

Vos 4 Points Cles

1

Deux routers, un service

Traefik permet de definir plusieurs routers pointant vers le meme backend nginx, chacun avec ses propres regles et middlewares.

2

Echappement des $ obligatoire

Dans docker-compose.yml, chaque $ du hash bcrypt doit etre double ($$) pour eviter l'interpretation comme variable.

3

Priorites explicites

Definissez toujours des priorites explicites (ex: 100 vs 10) pour garantir que le routeur specifique est evalue avant le routeur general.

4

Tester avec curl d'abord

Validez avec curl -I avant de tester dans le navigateur. Les codes 200/401 confirment que le routage fonctionne.


Besoin d'aide sur Traefik ?

Je propose des missions d'accompagnement sur l'infrastructure Docker, Traefik et l'automatisation DevOps.

Discutons de votre projet
Victor Langlois

Victor Langlois

consultant devops & cloud . expert ia

Fondateur d'Ops Imperium. Avec plus de 10 ans d'experience en infrastructure et automatisation, j'accompagne les equipes dans leur transformation DevOps et l'integration de l'IA dans leurs workflows.