This episode integrates Authelia with Traefik through the ForwardAuth middleware: defining the middleware in dynamic config, installing it via Docker labels, copying the Remote-User identity headers, up to a complete compose example that protects a service.

In episode 13 you built an authentication gateway in NGINX with auth_request. In episode 14 we apply the same pattern — proxy asks, Authelia answers — to the proxy most familiar with the container world: Traefik. Where NGINX requires manual config blocks, Traefik offers something different: a forwardAuth middleware attached per router, often configured entirely through Docker labels without touching a config file.
Traefik is a reverse proxy born in the container era: it reads service declarations from providers like Docker and determines routing based on labels attached to containers. That means securing an application with Authelia can be as simple as adding one middlewares label to that service.
An analogy: NGINX is a security officer managed through a thick manual; Traefik is a security officer who obeys command boards posted at each door. Both guard the same building — the difference is how we give instructions.
Middleware is Traefik's request-processing pipeline. forwardAuth works by sending a request to a specified address; the response determines the fate of the original request:
The core configuration in dynamic config (YAML file):
http:
middlewares:
authelia:
forwardAuth:
address: "http://authelia:9091/api/authz/forward-auth"
trustForwardHeader: true
maxResponseBodySize: 8192
authResponseHeaders:
- Remote-User
- Remote-Groups
- Remote-Email
- Remote-NameThe key options:
address — the Authelia endpoint. Note the path /api/authz/forward-auth, not the portal.trustForwardHeader — tells Authelia to trust the X-Forwarded-* headers Traefik sends. Authelia uses these headers to determine the scheme, host, and real IP for evaluating access control rules.authResponseHeaders — the Authelia response headers copied into the request forwarded to the application. This is the Trusted Header SSO mechanism: the application receives the user identity via Remote-User and friends.Traefik's advantage in Docker is that everything can be declarative through labels. The same middleware definition above can be written as labels:
services:
nextcloud:
image: linuxserver/nextcloud
labels:
traefik.enable: "true"
traefik.http.routers.nextcloud.rule: "Host(`nextcloud.example.com`)"
traefik.http.routers.nextcloud.entryPoints: "websecure"
traefik.http.routers.nextcloud.tls: "true"
traefik.http.routers.nextcloud.middlewares: "authelia@file"Note traefik.http.routers.nextcloud.middlewares: "authelia@file" — this is the magic line that attaches the authelia middleware to the nextcloud router. The @file suffix indicates the middleware is defined in a file provider (dynamic config), not via labels.
If you want to define the middleware via labels too (usually on the Authelia container), the pattern is the same:
labels:
traefik.enable: "true"
traefik.http.routers.authelia.rule: "Host(`auth.example.com`)"
traefik.http.routers.authelia.entryPoints: "websecure"
traefik.http.routers.authelia.tls: "true"
traefik.http.middlewares.authelia.forwardauth.address: "http://authelia:9091/api/authz/forward-auth"
traefik.http.middlewares.authelia.forwardauth.trustForwardHeader: "true"
traefik.http.middlewares.authelia.forwardauth.maxResponseBodySize: "8192"
traefik.http.middlewares.authelia.forwardauth.authResponseHeaders: "Remote-User,Remote-Groups,Remote-Email,Remote-Name"Here the labels prefixed with traefik.http.middlewares.* define the middleware, and the Authelia router itself passes through that middleware so the portal stays accessible. Other services just write middlewares: "authelia@docker" to point to a middleware from these labels.
Let's assemble everything: Traefik, Authelia, and one protected application.
networks:
net: {}
services:
traefik:
image: traefik:v3
restart: unless-stopped
networks: [net]
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./dynamic:/etc/traefik/dynamic
command:
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --providers.file.directory=/etc/traefik/dynamic
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
authelia:
image: authelia/authelia
restart: unless-stopped
networks: [net]
volumes:
- ./config:/config
labels:
traefik.enable: "true"
traefik.http.routers.authelia.rule: "Host(`auth.example.com`)"
traefik.http.routers.authelia.entryPoints: "websecure"
traefik.http.routers.authelia.tls: "true"
traefik.http.routers.authelia.middlewares: "authelia@file"
nextcloud:
image: linuxserver/nextcloud
restart: unless-stopped
networks: [net]
volumes:
- ./nextcloud:/config
- ./nextcloud/data:/data
labels:
traefik.enable: "true"
traefik.http.routers.nextcloud.rule: "Host(`nextcloud.example.com`)"
traefik.http.routers.nextcloud.entryPoints: "websecure"
traefik.http.routers.nextcloud.tls: "true"
traefik.http.routers.nextcloud.middlewares: "authelia@file"An important note: --providers.docker.exposedByDefault=false ensures only containers with traefik.enable: "true" are published — a practice that prevents services from being accidentally exposed to the public.
Both versions use the same concept. The practical differences you should know:
authResponseHeaders list to match what the application reads./api/authz/forward-auth on v3 (the modern ForwardAuth endpoint); the old /api/verify endpoint still works for compatibility, but isn't recommended for new installations.maxResponseBodySize — an option limiting the auth response body size, useful for preventing unexpected responses from filling memory.Warning
Make sure Traefik and Authelia are on the same Docker network (in the example: net). If they're separate, use the correct hostname in address — a wrong name means the middleware can never respond, and all requests to protected services will fail.
Once the stack is running:
nextcloud.example.com without logging in — you should be redirected to the auth.example.com portal.docker compose logs traefik to confirm the authelia@file middleware responds without errors.Remote-User header arrives — the application will recognize the user without a login form.This episode integrated Authelia with Traefik: understanding the forwardAuth middleware as a replacement for auth_request, defining middleware in dynamic config or Docker labels, choosing the address, trustForwardHeader, and authResponseHeaders options correctly, and assembling a complete compose setup that protects a service with a single label.
Traefik shows the power of the forward authentication pattern when combined with container-based declarations — one middleware definition, reused by all services. In episode 15 we look at the other end of the configuration spectrum: Caddy, the proxy known for its simple Caddyfile and automatic HTTPS. See you there!