Learn Authelia - Caddy Integration
Episode 15 of 31

Learn Authelia - Caddy Integration

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.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Introduction

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.

Caddyfile Structure: Portal and Application

A Caddyfile consists of site blocks. A basic example with the Authelia portal and one protected application:

LinuxCaddyfile — portal and 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.

The Role of Automatic HTTPS

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.

Adjusting the Authelia URL at the Proxy

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:

LinuxCaddyfile — explicit authelia_url
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:

LinuxCaddyfile — keeping the session cookie away from backends
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.

Testing the Integration

  1. Make sure both site blocks respond correctly over HTTPS — auth.example.com shows the portal, nextcloud.example.com redirects to the portal.
  2. Log in and complete MFA; you should be back in Nextcloud without a second login.
  3. Check the headers arriving at the application: Remote-User should contain the username, Remote-Groups the group list.
  4. If something's off, check the Caddy and Authelia logs — both will clearly record the sub-request path.

Closing

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!

Learn Authelia - Caddy Integration | Learn Authelia