Traefik’s Basic Auth middleware doesn’t actually perform authentication itself; it delegates the username/password verification to the client’s browser.
Let’s see Traefik in action protecting a simple whoami service.
First, we need a docker-compose.yml to spin up Traefik and our target service.
version: '3.8'
services:
traefik:
image: traefik:v2.9
container_name: traefik
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic_conf/:/etc/traefik/dynamic_conf/
networks:
- traefik-net
whoami:
image: traefik/whoami
container_name: whoami
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.routers.whoami.middlewares=basicauth@file"
- "traefik.http.middlewares.basicauth.basicauth.users=user:$$apr1$$h6uskkkW$$4MVI.Z2.0.8p.q.n4.x0" # user:password
networks:
traefik-net:
external: true
We’ll also need a traefik.yml to configure Traefik’s entrypoints and API.
log:
level: INFO
api:
dashboard: true
insecure: true
entryPoints:
web:
address: ":80"
The crucial part is the dynamic_conf/middlewares.yml file, which defines our Basic Auth configuration.
http:
middlewares:
basicauth:
basicAuth:
users:
- "user:$$apr1$$h6uskkkW$$4MVI.Z2.0.8p.q.n4.x0"
The username and password (user:password) are bcrypt-hashed. You can generate these using htpasswd:
echo $(htpasswd -nb user password)
# Output: user:$apr1$h6uskkkW$4MVI.Z2.0.8p.q.n4.x0
Now, when you navigate to http://whoami.localhost (you’ll need to add this to your /etc/hosts file or configure it in your DNS), your browser will prompt you for a username and password. Entering user and password will grant you access to the whoami service, displaying its request details.
The traefik.http.routers.whoami.middlewares=basicauth@file label tells Traefik to apply the middleware named basicauth defined in a file (@file) to the whoami router. Traefik reads the dynamic_conf/middlewares.yml file and finds the basicAuth configuration under http.middlewares.basicauth. When a request matches the whoami router’s rule, Traefik intercepts it and adds the Authorization: Basic ... header to the request before forwarding it to the whoami service. However, the actual validation happens client-side: the browser sends the credentials, and if they don’t match the stored hash, the browser shows the login prompt again. Traefik itself doesn’t store or validate the password; it simply passes the Authorization header through.
The most surprising thing about Traefik’s Basic Auth is that it’s fundamentally a client-side mechanism. Traefik doesn’t "know" the password or perform the cryptographic comparison. It simply relies on the browser’s implementation of HTTP Basic Authentication. The users list in the middleware configuration is essentially a list of valid credentials that the browser will compare against what the user enters. Traefik just forwards the Authorization header.
When you access http://whoami.localhost, Traefik sees the basicauth@file middleware attached to the whoami router. It checks if the incoming request has an Authorization header. If not, it returns a 401 Unauthorized response with a WWW-Authenticate: Basic realm="Restricted" header. The browser then pops up the login dialog. Upon successful entry, the browser constructs an Authorization: Basic <base64-encoded username:password> header and resends the request. Traefik receives this header, sees it’s present, and forwards the request as is to the whoami service. The whoami service itself does not perform any authentication; it just sees the request coming through.
The $$apr1$$h6uskkkW$$4MVI.Z2.0.8p.q.n4.x0 part is a bcrypt hash of the password "password" for the user "user". This hash is generated using htpasswd -nb user password. Traefik doesn’t decrypt this hash. It’s merely a string that is compared against the decoded value of the Authorization header. The comparison is done by the browser, not Traefik. Traefik’s role is to ensure the Authorization header is present for the client to process.
The next step is often securing these routes with TLS.