Traefik Forward Auth lets you offload authentication entirely to a separate service, and it works by having Traefik act as a dumb proxy for your authentication decisions.

Let’s see it in action. Imagine you have a few internal services that need basic authentication. Instead of building auth into each one, we’ll point Traefik to a dedicated auth service.

Here’s a simplified Traefik configuration snippet:

http:
  routers:
    my-app-router:
      rule: "Host(`myapp.example.com`)"
      service: "my-app-service"
      middlewares:
        - "auth-middleware@file"

  services:
    my-app-service:
      loadBalancer:
        servers:
          - url: "http://192.168.1.100:8080"

  middlewares:
    auth-middleware:
      forwardAuth:
        address: "http://auth-service.example.com/auth"
        authResponseHeaders:
          - "X-User-ID"
          - "X-User-Roles"

And here’s what a basic auth service might look like (this is a conceptual Python example, not production-ready):

from flask import Flask, request, jsonify, make_response

app = Flask(__name__)

@app.route('/auth')
def authenticate():
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return make_response('Unauthorized', 401)

    # In a real app, you'd validate the token/credentials here
    # For this example, we'll just check for a specific header value
    if auth_header == 'Bearer valid-token-123':
        user_id = 'user-abc'
        user_roles = 'admin,user'
        response = make_response('Authenticated', 200)
        response.headers['X-User-ID'] = user_id
        response.headers['X-User-Roles'] = user_roles
        return response
    else:
        return make_response('Unauthorized', 401)

if __name__ == '__main__':
    app.run(port=80) # Auth service listens on port 80 for this example

When a user tries to access myapp.example.com, Traefik intercepts the request. It doesn’t look at the request body or try to understand the application’s logic. Instead, it forwards the request (or a modified version of it, depending on configuration) to the address specified in the forwardAuth middleware.

The auth service then does its job: it inspects the incoming request (usually headers like Authorization), verifies the user’s credentials, and decides whether to grant access. If it grants access, it sends a success response (typically 2xx) back to Traefik. Crucially, it can also add custom headers (like X-User-ID and X-User-Roles in our example) to this response. Traefik then takes these headers and adds them to the original request before forwarding it to the backend service (my-app-service).

If the auth service denies access (by returning a 401 or 403), Traefik simply returns that error response directly to the user and never forwards the request to the backend service. This is the core of "delegate auth": Traefik is just a gatekeeper, and the actual gatekeepers are elsewhere.

The beauty here is that your backend services (my-app-service in this case) become incredibly simple. They don’t need to know how authentication works. They just need to trust that Traefik only forwards requests from authenticated users, and they can then inspect the headers (like X-User-ID) that Traefik passed through from the auth service to determine what that user is allowed to do. This significantly reduces the attack surface and complexity of your individual applications.

A common misconception is that forwardAuth requires the auth service to return the original request. It doesn’t. The auth service’s job is to authorize. It returns a status code and potentially new headers. Traefik then uses these to decide whether to forward the original request (with added headers) to the backend. The authResponseHeaders directive in Traefik is specifically for mapping headers from the auth service’s response to the request that Traefik then forwards to your application.

Once you have Traefik Forward Auth set up, the next logical step is to secure multiple applications with the same auth service, potentially using different rules and custom headers for each.

Want structured learning?

Take the full Traefik course →