Skip to content

Protecting an app with ForwardAuth

This example walks through protecting a self-hosted app (we’ll use Grafana) with GateKeeper using Traefik’s ForwardAuth middleware.

  • Grafana at https://grafana.example.com, accessible only to logged-in GateKeeper users.
  • GateKeeper at https://auth.example.com, handling all authentication.
  • Traefik routing traffic and enforcing auth.
services:
traefik:
image: traefik:v3
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.websecure.address=:443
- --certificatesresolvers.le.acme.tlschallenge=true
- --certificatesresolvers.le.acme.email=you@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
ports:
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
gatekeeper:
image: ghcr.io/yourorg/gatekeeper:latest
environment:
BASE_URL: https://auth.example.com
SECRET_KEY: your-32-char-secret-here
ADMIN_EMAIL: admin@example.com
ADMIN_PASSWORD: change-me
SMTP_HOST: smtp.example.com
SMTP_FROM: noreply@example.com
SMTP_TLS: starttls
volumes:
- gk_data:/data
labels:
- traefik.enable=true
- traefik.http.routers.gk.rule=Host(`auth.example.com`)
- traefik.http.routers.gk.entrypoints=websecure
- traefik.http.routers.gk.tls.certresolver=le
- traefik.http.services.gk.loadbalancer.server.port=8080
- traefik.http.middlewares.gk-auth.forwardauth.address=http://gatekeeper:8080/auth/verify
- traefik.http.middlewares.gk-auth.forwardauth.authResponseHeaders=X-Auth-User,X-Auth-Email
grafana:
image: grafana/grafana:latest
environment:
GF_AUTH_PROXY_ENABLED: "true"
GF_AUTH_PROXY_HEADER_NAME: X-Auth-Email
GF_AUTH_PROXY_HEADER_PROPERTY: email
GF_AUTH_PROXY_AUTO_SIGN_UP: "true"
labels:
- traefik.enable=true
- traefik.http.routers.grafana.rule=Host(`grafana.example.com`)
- traefik.http.routers.grafana.entrypoints=websecure
- traefik.http.routers.grafana.tls.certresolver=le
- traefik.http.routers.grafana.middlewares=gk-auth
volumes:
gk_data:
letsencrypt:
  1. A browser visits grafana.example.com.
  2. Traefik intercepts the request and calls http://gatekeeper:8080/auth/verify.
  3. If the user has a valid session, GateKeeper returns 200 with X-Auth-Email set.
  4. Traefik forwards the request to Grafana with the X-Auth-Email header.
  5. Grafana uses proxy auth to log the user in automatically based on the header.

If the user has no session, GateKeeper returns 401 and Traefik redirects the browser to /login?redirect_uri=https://grafana.example.com/.

If you build your own app, read the X-Auth-Email and X-Auth-User headers set by GateKeeper to identify the logged-in user:

# Flask example
@app.route('/')
def index():
user_email = request.headers.get('X-Auth-Email', 'anonymous')
return f'Hello, {user_email}'
// Go example
func handler(w http.ResponseWriter, r *http.Request) {
email := r.Header.Get("X-Auth-Email")
fmt.Fprintf(w, "Hello, %s", email)
}