This episode integrates Authelia with Caddy: the forward_auth directive that triggers a verification sub-request, the uri directive pointing to the Authelia endpoint, copying identity headers with copy_headers, and a complete Caddyfile example with automatic HTTPS.

In episode 14 you installed the ForwardAuth middleware in Traefik via Docker labels. This time we try the proxy that makes simplicity a principle: Caddy. Caddy is known for two rare advantages: an extremely concise configuration (the Caddyfile), and automatic HTTPS via Let's Encrypt without a single line for certificate handling.
For forward authentication, Caddy provides the built-in forward_auth directive. It does the same thing as NGINX's auth_request and Traefik's forwardAuth middleware: sends a sub-request to Authelia, then forwards or denies the request based on the response. The difference is that the syntax is much more concise — often just four to six lines inside a site block.
An analogy: if NGINX gives you a control panel with a hundred switches, and Traefik gives you a catalog of labels you can stick on, then Caddy gives you one big button that says "secure". Slightly less control — but far less that can go wrong.
A Caddyfile consists of site blocks. A basic example with the Authelia portal and one protected application:
# Authelia portal.
auth.example.com {
reverse_proxy authelia:9091
}
# Protected application.
nextcloud.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy nextcloud:80
}Let's break it down:
reverse_proxy authelia:9091 in the portal block — Authelia itself is an ordinary service; no special middleware is needed to reach it.forward_auth authelia:9091 — Caddy sends a sub-request to Authelia every time a request comes to this site.uri /api/authz/forward-auth — the verification endpoint being called. This drives Authelia's ForwardAuth flow, complete with access control rule evaluation.copy_headers Remote-User Remote-Groups Remote-Email Remote-Name — copies the identity headers from Authelia's response into the request forwarded to the application. This is the Trusted Header SSO bridge.The interesting part: Caddy by default doesn't trust headers from other proxies and cleans up headers that could be forged. That's safe default behavior — you only need to add the trusted_proxies directive if there's another proxy layer in front of Caddy.
One thing that makes Caddy special in the Authelia context: every site block automatically gets a TLS certificate from Let's Encrypt. No ssl.conf file, no manual renewal, no listen 443 line.
Why does this matter for Authelia? Because Authelia is a session-cookie system that's only secure over HTTPS. The Secure cookie (which we discussed in episode 7) won't be sent over plain HTTP, and the redirect between portal and application is guaranteed safe because both sides are already HTTPS. With Caddy, that requirement is met almost for free.
If you're testing in a lab without a public domain, Caddy can still serve with an internal CA — for example with the global tls internal directive for self-signed local certificates, suitable for home networks.
By default, when a request is denied, Caddy throws a redirect to the Authelia portal. The portal URL value comes from Authelia's session configuration (session.cookies[].authelia_url). This is the cleanest approach — a single source of truth on the Authelia side.
However, for specific needs, you can override it via a query parameter on the uri directive:
nextcloud.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth?authelia_url=https://auth.example.com/
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy nextcloud:80
}Although supported, the official documentation recommends configuring the URL on the Authelia side rather than overriding it at the proxy — with a single source of truth, there are no two places that can fall out of sync.
One consideration that's often overlooked: after login, the browser sends the Authelia session cookie to all matching subdomains — including protected applications. That application doesn't actually need to see the session cookie; it only needs the Remote-User header.
For highly sensitive applications, the session cookie can be stripped from the Cookie header before forwarding:
nextcloud.example.com {
forward_auth authelia:9091 {
uri /api/authz/forward-auth
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
}
reverse_proxy nextcloud:80 {
header_up Cookie "authelia_session=[^;]+" "authelia_session=_"
}
}This practice ensures that even if the application is compromised, it can't hijack the user's Authelia session. Cheap defense in depth.
Tip
After changing the Caddyfile, validate with caddy validate --config Caddyfile before reloading. This validation catches syntax errors earlier than loading a broken config directly.
auth.example.com shows the portal, nextcloud.example.com redirects to the portal.Remote-User should contain the username, Remote-Groups the group list.This episode integrated Authelia with Caddy: the forward_auth directive as the verification sub-request, the uri directive pointing to /api/authz/forward-auth, copying identity via copy_headers, automatic HTTPS that secures the session cookie, up to the optional practice of keeping the session cookie away from backends.
Caddy shows that forward authentication doesn't have to be complex — minimal syntax, good security by default, and one thing you can fully hand over: TLS. In episode 16, we look at the classic proxy often used in mid-scale infrastructure: HAProxy, with an ACL- and Lua-based approach. See you there!