Traefik middleware doesn’t just add functionality to requests; it fundamentally rewrites them, acting like a programmable assembly line for HTTP traffic.

Let’s see it in action. Imagine you have a simple web service running on port 8080, and you want to:

  1. Add a X-Forwarded-Proto header.
  2. Redirect any HTTP requests to HTTPS.
  3. Add a Server header to identify your Traefik instance.

Here’s a Traefik configuration that accomplishes this using middleware:

# traefik.yml
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false

api:
  dashboard: true

certificatesResolvers:
  myresolver:
    acme:
      email: "your-email@example.com"
      storage: "acme.json"
      httpChallenge:
        entryPoint: "web"

http:
  routers:
    myrouter:
      rule: "Host(`example.com`)"
      entryPoints:
        - "websecure"
      service: "myservice"
      tls:
        certResolver: "myresolver"
      middlewares:
        - "add-proto-header@file"
        - "redirect-to-https@file"
        - "add-server-header@file"

  services:
    myservice:
      loadBalancer:
        servers:
          - url: "http://your-actual-service:8080"

  middlewares:
    add-proto-header:
      headers:
        customRequestHeaders:
          X-Forwarded-Proto: "https"

    redirect-to-https:
      redirectScheme:
        scheme: "https"
        permanent: true # Use 301 for permanent redirects

    add-server-header:
      headers:
        customResponseHeaders:
          Server: "MyTraefikInstance"

In this setup, when a request for example.com hits Traefik on port 443:

  1. The add-proto-header middleware adds X-Forwarded-Proto: https to the request headers before it’s processed further.
  2. The redirect-to-https middleware checks the incoming scheme. If it were http, it would issue a 301 Moved Permanently redirect to the https version. Since the router is already on websecure (port 443), this middleware’s primary effect here is to ensure the outgoing request to the backend service has the correct scheme if it were somehow altered, or more importantly, it’s evaluated in the chain. The key is that the order matters.
  3. The add-server-header middleware adds Server: MyTraefikInstance to the response headers before sending it back to the client.

This demonstrates how middleware can modify both incoming requests and outgoing responses, allowing for complex request manipulation without touching your backend application code.

The core problem Traefik middleware solves is the separation of concerns for HTTP request/response handling. Instead of embedding logic for authentication, rate limiting, header manipulation, or URL rewriting into every single backend service, Traefik centralizes these cross-cutting concerns. This makes your backend services leaner and focused solely on their business logic, while Traefik acts as a sophisticated API gateway or reverse proxy that orchestrates and enhances the traffic flow.

Internally, Traefik processes middleware in a strict, defined order for each router. When a request arrives, Traefik identifies the matching router. Then, it iterates through the middlewares listed for that router, executing each one sequentially. If a middleware modifies the request (e.g., adds a header, redirects), the subsequent middleware in the chain operates on that modified request. For response manipulation, the middleware also executes in order, but the headers are typically added to the outgoing response. The redirectScheme middleware, for instance, will cause Traefik to stop further processing of the request and immediately send a redirect response if the scheme doesn’t match.

The chain middleware is a powerful concept that allows you to group multiple individual middleware configurations into a single, reusable unit. This is invaluable for applying the same set of transformations to multiple routers without repetition. You define a chain middleware, and then list the names of other middleware configurations (which can themselves be individual middleware or other chains) that Traefik should execute in order.

For example, to apply the previous set of transformations to another router, you could define:

# traefik.yml (continued)
http:
  middlewares:
    # ... existing middlewares ...

    my-common-transforms:
      chain:
        middlewares:
          - "add-proto-header@file"
          - "redirect-to-https@file"
          - "add-server-header@file"

  routers:
    # ... existing router ...
    another-router:
      rule: "Host(`another.example.com`)"
      entryPoints:
        - "websecure"
      service: "anotherservice"
      tls:
        certResolver: "myresolver"
      middlewares:
        - "my-common-transforms@file"

Here, my-common-transforms bundles three existing middleware configurations. When another-router uses my-common-transforms, Traefik executes add-proto-header, then redirect-to-https, and finally add-server-header for requests matching another.example.com. The @file suffix tells Traefik to look for these middleware definitions in the static configuration file (traefik.yml or traefik.toml).

A subtle but critical point about middleware chaining is that some middleware can terminate the request processing early. For example, redirectScheme or basicAuth middleware, if they trigger a redirect or an authentication failure, will send a response immediately and stop any subsequent middleware from being processed for that request. This is by design, preventing sensitive operations or unnecessary processing after a definitive action has been taken.

The next step in understanding Traefik’s request processing pipeline involves exploring how different middleware types interact, especially in scenarios involving authentication, rate limiting, and custom plugins.

Want structured learning?

Take the full Traefik course →