Help Needed: Traefik + Authelia Behind Cloudflare Tunnel Always Returns 401

Hello everyone,

I’ve been struggling to get Authelia’s forward-auth flow working behind a Cloudflare Tunnel and Traefik v3.0. Every request to my protected service immediately returns 401 Unauthorized instead of redirecting me to the Authelia login page. Below is a complete overview of my setup, with secrets redacted. Any pointers would be hugely appreciated!


1. Environment & Versions

  • Docker & Docker Compose: Docker Engine 24.x, Compose v2.x
  • Traefik: v3.0 (Docker provider + file provider)
  • Authelia: v4.37.5
  • Cloudflare Tunnel: Official cloudflare/cloudflared:latest
  • Protected services: Vaultwarden, Photoprism

2. docker-compose.yml

version: "3.8"
services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    command:
      - --api.dashboard=true
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.file.filename=/etc/traefik/middlewares.yml
      - --providers.file.watch=true
      - --log.level=DEBUG
    environment:
      - CF_DNS_API_TOKEN=<CLOUDFLARE_API_TOKEN>
    volumes:
      - ./traefik/middlewares.yml:/etc/traefik/middlewares.yml:ro
      - ./traefik/letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - infra_net
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.xxxxx.com`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=cloudflare"

  authelia:
    image: authelia/authelia:4.37.5
    container_name: authelia
    restart: unless-stopped
    expose:
      - "9091"
    volumes:
      - ./authelia/config:/config:ro
    networks:
      - infra_net
    depends_on:
      redis:
        condition: service_healthy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.authelia.rule=Host(`authelia.xxxxx.com`)"
      - "traefik.http.routers.authelia.entrypoints=websecure"
      - "traefik.http.routers.authelia.tls.certresolver=cloudflare"
      - "traefik.http.services.authelia.loadbalancer.server.port=9091"

  redis:
    image: valkey/valkey:8-alpine
    container_name: authelia-redis
    healthcheck:
      test: ["CMD", "valkey-server", "--version"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    networks:
      - infra_net
    volumes:
      - ./authelia/redis:/data

  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    expose:
      - "80"
    volumes:
      - ./vaultwarden/data:/data
    networks:
      - infra_net
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.xxxxx.com`)"
      - "traefik.http.routers.vaultwarden.entrypoints=websecure"
      - "traefik.http.routers.vaultwarden.tls=true"
      - "traefik.http.routers.vaultwarden.tls.certresolver=cloudflare"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
      - "traefik.http.routers.vaultwarden.middlewares=\
cloudflare-whitelist@file,\
inject-original-url@file,\
authelia@file,\
auth-redirect-vault@file"

  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    command: tunnel run --token <TUNNEL_TOKEN>
    networks:
      - infra_net

networks:
  infra_net:
    driver: bridge

3. traefik/middlewares.yml

http:
  middlewares:

    # 1. Whitelist Cloudflare IPs + Docker network
    cloudflare-whitelist:
      ipAllowList:
        sourceRange:
          - 103.21.244.0/22
          - 104.16.0.0/13
          # … other CF ranges …
          - 172.29.44.0/22   # Docker subnet of cloudflared

    # 2. Inject X-Original-URL for Authelia
    inject-original-url:
      headers:
        customRequestHeaders:
          X-Original-URL: "https://${req.host}${req.uri}"

    # 3. ForwardAuth to Authelia
    authelia:
      forwardAuth:
        address: http://authelia:9091/api/verify
        trustForwardHeader: true
        authResponseHeaders:
          - Remote-User
          - Remote-Groups
          - Remote-Name
          - Remote-Email

    # 4. Error redirect for 401 → Authelia UI
    auth-redirect-vault:
      errors:
        status:
          - "401"
        service: authelia@docker
        query: "/api/redirect?rd=https://vaultwarden.xxxxx.com"

4. authelia/config/configuration.yml

server:
  host: 0.0.0.0
  port: 9091

access_control:
  default_policy: one_factor
  rules:
    - domain: vaultwarden.xxxxx.com
      policy: one_factor
    - domain: authelia.xxxxx.com
      policy: bypass

session:
  name: authelia_session
  secret: "<SESSION_SECRET>"
  domain: xxxxx.com

storage:
  local:
    path: /config/db.sqlite3

notifier:
  filesystem:
    filename: /config/notification.txt

5. What’s Happening

  • Every request to https://vaultwarden.xxxxx.com returns 401 Unauthorized immediately.
  • I expect Traefik → ForwardAuth → Authelia UI (when not logged in) → after login I get back to Vaultwarden.

6. Logs & Diagnostics

  1. Traefik debug log shows lines like:
    Rejecting IP 172.29.44.5: no match in ipAllowList
    
  2. I’ve confirmed my Cloudflared container IP is inside 172.29.44.0/22 via:
    docker network inspect infra_net
    
  3. Order of middlewares on the Vaultwarden router:
    1. cloudflare-whitelist
    2. inject-original-url
    3. authelia (forwardAuth)
    4. auth-redirect-vault (errors)

7. What I’ve Tried

  • Adding all Cloudflare IPv4 & IPv6 ranges to ipAllowList
  • Including the Docker subnet (172.29.44.0/22)
  • Swapping middleware order
  • Verifying X-Original-URL header is injected correctly

8. Questions

  1. Am I missing a Traefik middleware ordering detail?
  2. Should I use inflightreq or another Traefik router option?
  3. Any gotchas with Cloudflare Tunnel IPs vs Docker overlay?

Thanks in advance for any guidance!

I don't think Traefik dynamic configuration supports runtime variables like ${req.host} or ${req.uri} in header values or elsewhere.

    # 2. Inject X-Original-URL for Authelia
    inject-original-url:
      headers:
        customRequestHeaders:
          X-Original-URL: "https://${req.host}${req.uri}"

Enable and check Traefik access log in JSON format (doc), what’s the output during requests?

Hello,
I enabled the access logs in JSON format as you suggested and confirmed that the X-Original-URL header does not appear in the logs. It looks like Traefik indeed doesn't interpret ${req.host} or ${req.uri} as dynamic runtime variables — the value is treated as a literal string.

I’ll try to change the way I’m handling this and look for an alternative method to pass the original URL to Authelia.
Thanks a lot for the insight!

Traefik automatically passes a lot of X-Forwarded-… headers already.