# Fix Traefik middlewares that don't run: provider namespaces, chain order, and the headers overwrite trap

Fix Traefik middleware failures by class: dead cross-provider references (middleware X@file does not exist), chain execution order (auth vs rate limit vs redirect), the headers middleware Set-not-merge overwrite trap, and Router defined multiple times conflicts.

Exact reference: {"kind":"skill_version","skill_id":"skl_55CzvmqStG_wSdjN6EKFug","version_id":"skv_AvOjFy3ysUnf0TlgcCEzIQ"}

Applicability: [{"constraint":">=2.0","technology":"Traefik","version_scheme":"semver"}]

# Fix Traefik middlewares that don't run: provider namespaces, chain order, and the headers overwrite trap

Use this when a Traefik middleware seems to do nothing, the dashboard shows
the router disabled, or chaining two middlewares gives surprising results.
Middleware failures fall into three distinct classes — a dead reference, a
wrong order, or a merge that never happens — and each has a different fix.

## 1. Classify: dead reference, wrong order, or silent overwrite

Check the dashboard (or `GET /api/http/routers`) for your router:

- Router **disabled** with an error like
  `middleware "authelia-forwardauth@file" does not exist` → **dead
  reference**. Go to step 2.
- Router enabled, middleware listed, but auth/redirect/rate-limit never
  triggers → the middleware is attached to the **wrong router**, or a
  **chain runs in an unexpected order**. Go to step 3.
- Router enabled, two `headers` middlewares chained, but only one set of
  headers appears on the response → the **headers overwrite trap**. Go to
  step 4.
- Error `Router defined multiple times with different configurations` →
  the same router name is defined on two containers with different
  middleware lists. Go to step 5.

## 2. Dead reference: the `@provider` namespace

Every middleware lives in a provider namespace, and a bare name resolves in
the **router's own provider**. A router built from Docker labels that
references a middleware defined in the file provider must qualify it:

```yaml
# WRONG: resolves to authelia@docker, which does not exist -> router disabled
- "traefik.http.routers.myapp.middlewares=authelia"
# RIGHT: the middleware is defined in the file provider
- "traefik.http.routers.myapp.middlewares=authelia@file"
```

Rules:

- From Docker labels, file-provider middlewares need the `@file` suffix;
  Docker-defined middlewares referenced from the file provider need
  `@docker`.
- The error names the exact missing reference
  (`middleware "authelia-forwardauth@file" does not exist`) — trust it and
  check that the defining file is actually loaded (mounted, valid YAML,
  watched by the file provider).
- Middleware names are global within a provider. Defining the same
  middleware name in two providers creates two distinct middlewares.

## 3. Order: chains run in list order on the request path

Middlewares listed on a router execute **in the order they are listed**
for the request; a `chain` middleware runs its members in its own list
order. Response-side effects (response headers) apply in reverse as the
response unwinds.

```yaml
# request passes simple_ratelimit first, then digest_auth, then the service
- "traefik.http.middlewares.secured_chain.chain.middlewares=simple_ratelimit,digest_auth"
- "traefik.http.middlewares.simple_ratelimit.ratelimit.average=5"
- "traefik.http.middlewares.simple_ratelimit.ratelimit.period=5s"
- "traefik.http.middlewares.simple_ratelimit.ratelimit.burst=2"
- "traefik.http.routers.whoami_route.middlewares=secured_chain"
```

Practical consequences:

- Put **auth before rate limiting** if unauthenticated requests shouldn't
  consume quota — or rate limit first if you want to blunt brute force.
  The choice is yours, but it must be deliberate: list order is the policy.
- A `redirectRegex`/`redirectScheme` middleware **before** auth means
  redirects happen without credentials; after auth means the redirect only
  fires for authenticated requests.
- The `chain` type is just a named list — it has no other semantics. Any
  middleware referenced inside a chain follows the same `@provider`
  namespace rule as step 2.

## 4. The headers overwrite trap: `Set`, not merge

The `headers` middleware applies Go's header `Set` semantics: for each
configured header it **overwrites** any existing value. It never appends and
never merges. Consequences that bite people:

- Two chained `headers` middlewares that set the **same header key** —
  the later one in the chain wins, silently.
- A `headers` middleware that sets `Content-Security-Policy` **replaces**
  the backend's own `Content-Security-Policy` response header; there is no
  option to concatenate the two values.
- Fix: combine all the headers for one concern into a **single**
  `headers` middleware (one global security-headers middleware, one
  per-service middleware only when its keys are disjoint), e.g.:

```yaml
# file provider: one middleware holding all security headers
http:
  middlewares:
    secure-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
        contentTypeNosniff: true
        referrerPolicy: "strict-origin-when-cross-origin"
        customResponseHeaders:
          X-Robots-Tag: "none,noarchive"
```

The dashboard showing both middlewares as "enabled, no errors" does not
contradict this — both run; the later `Set` simply wins.

## 5. Router defined multiple times

Middlewares attach to a **router**, not a host. If two containers define
the same router name (`traefik.http.routers.backend...`) with different
middleware lists, Traefik rejects the conflict:

```
Router defined multiple times with different configurations
```

Fix: give each container's router a unique name
(`traefik.http.routers.backend1`, `traefik.http.routers.backend2`), each
with its own middleware list. Containers behind one router must share the
identical router configuration, including middlewares.

## 6. Checklist

1. Dashboard/API: is the router enabled? Copy the exact error text.
2. `middleware "X@provider" does not exist` → qualify the reference with
   `@file`/`@docker` and confirm the defining config is loaded (step 2).
3. Middleware runs but order matters (auth vs rate limit vs redirect) →
   reorder the list; the list is the policy (step 3).
4. Chained `headers` middlewares clobbering each other → merge into one
   `headers` middleware per key-space; remember `Set`, never merge (step 4).
5. `Router defined multiple times` → unique router names per container,
   middleware lists identical per router (step 5).


## Supporting basis and limitations

Built from the Traefik provider-namespace documentation, the chain middleware reference, the headers middleware source behavior (Set/delete, no merge) confirmed by community threads, and real dashboard error strings (middleware does not exist, Router defined multiple times with different configurations).

## Change and rationale

New skill: fix Traefik middlewares that don't run — provider namespaces, chain order, headers overwrite.

Middleware questions recur because the failure modes look identical (middleware silently not applied) but have unrelated causes: a missing @provider suffix disables the router, list order is the security policy, and the headers middleware overwrites instead of merging. A classify-first procedure stops the usual fix of reordering labels at random.
