Setup: Authentik's embedded outpost (no separate ghcr.io/goauthentik/proxy
container) protecting an app on a different host than Authentik, via
Caddy's forward_auth. Community examples assume Caddy and Authentik are on
the same machine — the two-host case has two extra gotchas that silently
bypass auth instead of erroring.
Symptom: config validates fine, Provider/Application exist and are bound to the embedded outpost — but every request reaches the protected app, no matter the auth state. No redirect, no error.
Adding header_up Host {host} (to preserve the original host for Authentik)
breaks Host B's own Caddy routing, which is keyed on auth.example.com. A
request with Host: app.example.com matches nothing there, and Caddy's
default for "nothing matched, no catch-all" is Go's http.ResponseWriter
default: an empty 200 OK — not 404, not 502. forward_auth sees 2xx and
waves the request through.
Fix: don't override Host. Caddy's default rewrite to the upstream host
is what you want here.
With Host fixed, requests reach Authentik but now always get a 404 "no app
for hostname". Authentik picks the Provider via X-Forwarded-Host (preferred
over Host — see internal/utils/web/host.go). Host A sets it correctly, but
Host B's Caddy overwrites X-Forwarded-Host on its own hop to
127.0.0.1:9000 (Caddy ignores incoming X-Forwarded-* by default, to
prevent spoofing).
Fix: trust Host A on Host B's Caddy so it stops overwriting the header.
Host A (app.example.com, protected app):
(authentik_forwardauth) {
reverse_proxy /outpost.goauthentik.io/* https://auth.example.com
forward_auth https://auth.example.com {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
trusted_proxies private_ranges
}
}
app.example.com {
import authentik_forwardauth
reverse_proxy 127.0.0.1:8080
}No header_up Host override anywhere — that's the point.
Host B (auth.example.com, Authentik):
auth.example.com {
reverse_proxy 127.0.0.1:9000 {
trusted_proxies <HOST_A_PUBLIC_IP>/32
}
}