This episode covers centralized authentication: the forward_auth directive, integration with Authelia, Authentik, OAuth2 Proxy, and Keycloak, forwarding headers from the auth response, and global auth patterns with snippets to protect many applications.

Basic auth is fine for a single site, but what if you have ten applications that all need login? Building authentication into each app is tedious and inconsistent. The solution: forward auth — Caddy forwards every request to a centralized authentication service, which decides whether to accept or reject it.
Episode 18 covers the forward_auth directive, integration with popular providers like Authelia, Authentik, OAuth2 Proxy, and Keycloak, how to copy headers from the auth response to your application, and global auth patterns with snippets to protect many applications at once.
This is the modern way to protect a homelab and internal services: one login, every application secure.
The forward auth flow in Caddy:
The advantage: the application doesn't need to know anything about authentication. Caddy and the auth service handle it.
forward_auth uses the auth service URL:
app.example.com {
forward_auth localhost:9091 {
uri /api/verify?rd=https://login.example.com
copy_headers Authorization Remote-User
}
reverse_proxy localhost:8080
}uri sets the verification endpoint on the auth service.copy_headers copies headers from the auth response to the request forwarded to the application.forward_auth localhost:9091 points to the auth service running locally.
copy_headers Remote-User tells the application who the user is. The application reads the Remote-User header to learn the identity without implementing its own login. This is the standard pattern when using Authelia or OAuth2 Proxy.
Authelia is a popular auth service for homelabs:
{
snippet authelia {
forward_auth authelia:9091 {
uri /api/authz/forward-auth?authelia_url=https%3A%2F%2Fauth.example.com
copy_headers Remote-User Remote-Groups Remote-Email
}
}
}
app.example.com {
import authelia
reverse_proxy localhost:8080
}
internal.example.com {
import authelia
reverse_proxy localhost:9000
}The authelia snippet is reused across many sites. Authentik uses a similar pattern with its /api/v1/auth/flow/... endpoint. With a single configuration, every application that imports the snippet is protected.
OAuth2 Proxy bridges to OAuth2 providers:
{
snippet oauth {
forward_auth localhost:4180 {
uri /oauth2/auth
copy_headers X-Auth-Request-User X-Auth-Request-Email
}
}
}
app.example.com {
import oauth
reverse_proxy localhost:8080
}Keycloak can be used directly or through OAuth2 Proxy. The same pattern applies: forward auth in front, identity headers behind.
Single Sign-On (SSO) allows one login for all applications. The components:
With Caddy in front of every application, no application handles login itself — consistency is guaranteed.
Secure and HttpOnly.Not every path needs protection:
app.example.com {
@public {
path /public/* /assets/*
}
forward_auth localhost:9091 {
uri /api/verify
copy_headers Remote-User
}
@proteksi {
not path /public/* /assets/*
}
handle @proteksi {
reverse_proxy localhost:8080
}
handle {
root * /var/www
file_server
}
}The /public/* pages and static assets stay open; everything else is proxied after auth. Notice the @proteksi matcher with not — the inverse of the public paths.
The most common pattern: some content public, some requiring login. The combination of the @public and @proteksi matchers above is the blueprint. Public serves static files; protected serves the application.
Episode 18 opened up centralized authentication: the forward auth concept, the forward_auth directive with uri and copy_headers, Authelia, Authentik, OAuth2 Proxy, and Keycloak integration, SSO with centralized sessions and logout, and snippet patterns to protect many applications at once.
Key takeaways:
copy_headers sends the user identity to the application.not matcher limits the protected area.In the next episode, episode 19, we'll cover IP filtering & access control — the remote_ip matcher with CIDR, whitelists and blacklists, trusted_proxies for real IP detection, GeoIP filtering with plugins, and rate limiting for basic DDoS protection.