authentik forward auth with Caddy: when a proxy in front of the app is the right fit
Forward auth with Caddy places authentik in front of an application that cannot speak OIDC or SAML. Caddy receives each request, asks the authentik proxy outpost whether the user is signed in and authorized, and only forwards the request to the application when that check passes. The application itself stays unchanged, which is the main reason a team picks this pattern over native SSO. The pattern fits best when the application has no SSO support, when its vendor will not modify the login code, or when the application trusts a header or sits on a network segment that should never be reachable directly. It is a weaker fit when the application already supports OIDC or SAML, because native SSO gives the app its own session, its own user records and its own logout handling. Forward auth also depends on every request passing through the proxy, so a team should confirm that nothing else, such as a direct port or an internal service address, can reach the app.
The authentik proxy outpost does the authentication work in this setup. Caddy handles TLS termination for the public hostname, so certificates, HTTP to HTTPS redirects and protocol negotiation stay in Caddy, while the outpost decides whether a session is valid. Before you commit, confirm which outpost version matches your authentik server, how the outpost reaches the authentik server and how the outpost is deployed in your environment. Read the authentik providers documentation to confirm how the proxy provider type is configured, then plan the cookie domain and the paths that Caddy must route to the outpost.
Which forward auth mode fits each application situation
A single application on its own hostname is the simplest case. You create one proxy provider for that host, bind it to an application in authentik, and point a Caddy forward_auth block at the outpost. The gain is a narrow blast radius: the session cookie and the access policy apply to one name, and a change to one application's policy cannot affect another. The cost is that every new hostname needs its own provider, application and Caddy block, which adds administration as the estate grows.
- Narrow cookie scope: the session cookie is set for the single hostname, so no other application receives it.
- Per-application policy: bindings and policies can differ cleanly for this one app.
- More objects to maintain: each hostname adds a provider, an application and a route to keep in sync.
- Clear ownership: the application owner and the access reviewer look at one provider.
- Logout scope: sign-out affects only this application's session unless you link it to others.
Before you commit, confirm with your team how many hostnames will need protection in the next planning cycle, because the per-host overhead is the main recurring cost.
When several applications share a parent domain, such as wiki.example.internal and tickets.example.internal, a domain-level provider lets one authentication session cover all of them. The gain is a single sign-on experience across those hosts with fewer objects to manage. The cost is that the session cookie is shared across the domain, so any application under that parent inherits the trust established by a login elsewhere, and access control depends more heavily on the policies you attach.
- One session for many hosts: users sign in once for the whole parent domain.
- Shared cookie exposure: a cookie scoped to the parent domain is sent to every subdomain, so a weaker subdomain widens the risk.
- Policy discipline: you must enforce per-application authorization, because authentication alone no longer separates the apps.
- Cookie domain planning: the cookie domain must match your hostnames exactly, and mismatches cause login loops that are hard to diagnose.
- Subdomain inventory: every new subdomain under the parent becomes part of the trust boundary.
Confirm with your security team which subdomains exist, who controls them, and whether any of them run software that you do not fully govern before you widen the cookie scope.
Applications that support OpenID Connect or SAML natively should usually be integrated through those protocols rather than placed behind a proxy. The gain is that the application itself validates tokens or assertions, receives standard claims, and manages its own session, so Caddy does not sit in the authentication path. The cost is configuration on both sides: each application needs its client or SAML settings, and you must maintain certificates, redirect URIs and attribute mappings. Forward auth remains a fallback for apps that cannot do this.
- Protocol-level trust: the application verifies identity itself, which is easier to explain to auditors than header trust.
- No proxy dependency for login: the Caddy route handles only traffic to protected pages, not the identity exchange.
- Per-application configuration: redirect URIs, signing keys and claim mappings must be kept current.
- Session ownership moves: session lifetime and logout behavior depend on the application, so verify them individually.
- Mixed estates: running both forward auth and native SSO increases the number of login paths to test.
Before you choose, check the application's documentation for OIDC or SAML support and confirm with the vendor which protocol features and claims it accepts.
Configuration options in the Caddy and authentik setup
This table compares the decisions that shape a Caddy and authentik forward auth build, with the option, when it fits and what to confirm. Read it before writing the Caddyfile, because the outpost hosting choice and the header list determine most of the later configuration.
| Decision | Options | When it fits | Confirm before building |
|---|---|---|---|
| Outpost hosting | Embedded outpost in the authentik server, or a standalone proxy outpost container | Embedded suits a small lab or a single host where Caddy can reach the server directly. A standalone outpost suits separate networks or where you want the outpost scaled and hardened apart from the server. | Whether your authentik version still ships the embedded outpost in the expected form, and which outpost image tag matches the server version |
| Outpost path Caddy routes to authentik | Dedicated path prefix on each protected host, such as the outpost path under the application domain | Use when the application and the outpost share one hostname, so the login and callback endpoints stay on the same origin as the app. | The exact outpost path prefix in the authentik proxy provider documentation, and that Caddy sends only that prefix to the outpost without catching application routes |
| Identity headers copied upstream | Headers set by the outpost, such as user name, email and groups, copied by Caddy into the request to the application | Copy only the headers the application needs. Applications that trust headers should be reachable only through Caddy. | Header names the outpost version sets, and that Caddy strips any client-supplied copies of those headers so users cannot forge them |
| Provider mode | Single application (one provider per hostname) or domain-level (one provider for a parent domain) | Single application keeps policies and cookies separate per app. Domain-level suits many apps under one parent domain and a shared session, at the cost of a broader cookie scope. | How the cookie domain and authorization prompt behave for the chosen mode in your authentik version |
| TLS termination | Caddy terminates TLS for public hostnames and talks plain HTTP to the outpost or upstream on a private network | Fits most single-host and private-network setups where Caddy is the only public entry point. | Whether the link between Caddy and the outpost or application is on a trusted network, and whether that hop needs its own TLS |
| Caddy directive version | forward_auth directive in the Caddy version you run, with copy_headers listing the identity headers | Use the directive for the Caddy release you deploy, since option names and defaults can change between releases. | Syntax and supported options in the Caddy documentation for your exact release |
| Authentication failure behavior | Redirect unauthenticated users to the login flow, or return an error status to the client | Browser-based applications normally need redirects to the sign-in page. API clients usually need a status response they can handle. | How the outpost answers unauthenticated requests on the paths your API clients use |
| Authentik version and upgrade path | Pin to a supported release line and align the outpost version with the server | Pinning keeps the header names and endpoints you tested stable during upgrades. | Supported versions in the authentik security policy and the release notes for any version you plan to move to |
How to wire authentik outposts into a Caddy site
This sequence connects one Caddy site to an authentik proxy provider so that Caddy checks every request with the outpost, redirects anonymous users to sign in, and passes the verified identity headers to the application. Each step ends with a check you can run before moving on.
Create the proxy provider and application
In the authentik admin interface, an administrator creates a proxy provider in forward auth mode for the hostname that users will open, such as app.example.com, and sets the external host to match the address users see in the browser. The administrator then creates an application that uses this provider, binds it to the groups or policies that may reach the site, and assigns the provider to the embedded or a standalone outpost. The step is done when the application appears in the library with the provider attached and the outpost lists it. Confirm the exact field names and the forward auth mode options against the Providers documentation for your authentik version before you save.
Deploy the proxy outpost next to Caddy
Decide whether the outpost runs as the embedded outpost inside the authentik server or as a standalone proxy outpost that Caddy can reach over the network. A standalone outpost is usually easier to place close to the application, and it is started with an outpost token copied from the authentik interface. Your platform team runs the container with Docker Compose or Kubernetes and makes sure the outpost can reach authentik and Caddy can reach the outpost on its listening port. The step is done when the outpost shows as connected in the authentik admin interface and its health endpoint responds from the Caddy host.
Add the forward_auth block to the Caddy site
Your team adds a forward_auth directive inside the site block for the protected hostname. The directive points at the outpost address and the outpost path that authentik serves for the provider, and it lists the identity headers to copy with copy_headers, such as the username, email, groups and the authentik-specific headers documented for the proxy provider. Caddy will forward the request to the outpost, and only a successful check lets it reach the upstream. Check the header names and outpost path against the current proxy documentation, because they can change between authentik releases. The step is done when caddy validate reports no errors and the configuration reloads without warnings.
Route the outpost path to authentik
Caddy must send the outpost path, which handles sign-in, callbacks and session checks, to the outpost instead of the application. Your team adds a handle or handle_path block for that path that reverse proxies to the outpost, and keeps the application upstream in a separate handle block that is protected by forward_auth. Without this route, sign-in redirects loop or return errors. The step is done when a request to the outpost path returns a response from authentik rather than from the application, and the application path is not reachable without going through the check.
Test an unauthenticated request
From a client with no session, your team requests the protected hostname and records the response. A correct setup returns a redirect to the authentik sign-in page for the application rather than the application content. Your team also checks that the redirect target uses the public hostname and HTTPS, because a wrong external host in the provider is a common cause of redirects to an internal address. The step is done when an anonymous request is redirected to sign-in and no application content is returned.
Test sign-in and access through the site
A test user signs in through the authentik login page, which should accept the account and any MFA the policies require, and then returns to the protected hostname. The test should use a user who is allowed by the application bindings and, if possible, a second user who is not, so the denial path is also confirmed. The step is done when the allowed user reaches the application, the denied user sees an access error from authentik, and the session persists across page loads as expected for your session settings.
Verify identity headers at the upstream
The application or a small echo service behind Caddy should log the request headers it receives. Your team confirms that the headers listed in copy_headers arrive with values that match the signed-in user, and that the upstream does not accept the same headers from a client that bypasses Caddy. Stripping any client-supplied copies of these headers at Caddy before forward_auth runs is part of the same check. The step is done when the upstream logs show the expected user and group values for the test account, and a request sent directly with spoofed headers does not change the identity the application sees.
Security risks in header-based forward auth
Forward auth works only if the application can trust the identity headers it receives. The risks below all come from one fact: Caddy and the authentik outpost make the access decision, and anything that bypasses them or alters what they pass along breaks the chain. Each card names one risk and the control that closes it.
Direct access to the application
If the application listens on a network address that users or other hosts can reach, a request can arrive without passing through Caddy and carry any identity header the client chooses. Bind the application to a Docker network or loopback address that only Caddy can reach, and remove any published host port. Test this by requesting the application's internal address from outside the proxy and confirming the connection fails.
Spoofed identity headers from clients
Caddy's forward_auth copies identity headers from the outpost response onto the upstream request, but a client may also send the same header names directly. Configure Caddy to strip any incoming identity headers before forward_auth runs, so only values returned by authentik reach the application. Confirm this by sending a request with a forged header and checking what the application receives.
Cookie domain scope
The authentik session cookie is set for a domain, and a broad cookie domain can expose it to sibling hostnames you do not control or run less carefully. Keep the cookie domain as narrow as your layout allows and review which hostnames share it. Ask the team that owns each sibling service whether it can read or set cookies on that domain before you widen the scope.
Outpost reachability
Caddy sends each authorization check to the outpost, so the outpost must be reachable from Caddy but not from the internet. Expose its port only on the internal network and do not publish it through a public route. Note that the outpost also depends on a working connection to authentik, so an unreachable authentik core will block logins or deny access depending on your configuration.
TLS between Caddy and authentik
Caddy terminates TLS for users, but the hop from Caddy to the outpost and from the outpost to the authentik core carries session data and identity information. If these hops cross a shared or untrusted network, use TLS on them or keep them on an isolated host network. Decide this with your security team and document the choice, since the outpost hop is the one most often left on plain HTTP.
Outpost token and secret handling
The outpost authenticates to authentik with a token, and anyone holding that token can act as the outpost. Store it in your secret manager or a restricted environment file, rotate it when staff or hosts change, and keep it out of Caddyfiles committed to version control. Check that only the Caddy and outpost containers can read the file.
Running and upgrading the outpost with authentik releases
The outpost has to run the same authentik version as the server, so every authentik upgrade is also an outpost upgrade. Treat the outpost as part of the authentik release, not as a separate component you can update on its own schedule. authentik names its releases by year and month, and the GitHub releases page is the place to see the current version and its publication date before you plan a change. Pin the outpost image to the same tag as the server in your Compose file or Helm values, so a mismatch does not appear quietly when someone pulls latest on one host and not another.
Check the authentik security policy before choosing a target version. It lists which versions receive security fixes, and the project announces security releases at least 24 hours in advance, so your change window should allow for an unscheduled patch. If your running version is no longer on that list, plan the move to a supported release as a priority item. Confirm with the authentik release notes whether the target version changes the outpost configuration, the proxy provider settings or the database requirements, because an outpost that starts cleanly can still fail to refresh its configuration from the server.
Monitor the outpost separately from the server. Watch the container health, its logs for failed connections to the authentik server, and the forward auth response rate at Caddy, since a stopped outpost typically shows up as errors at the proxy rather than at the login page. Keep a rollback plan that records the previous server and outpost image tags, a copy of the Caddyfile and the Compose or Helm values, and a tested database backup taken before the upgrade. Confirm with authentik's upgrade documentation whether a database migration can be reversed, because if it cannot, rollback means restoring the backup, not redeploying the old image. Before any upgrade, confirm the target version, the outpost image tag, the migration notes for your installed version, and who can approve a rollback during the change window.
Limits to confirm in your environment
Forward auth with Caddy depends on four things that vary by version and by host: the Caddy release and its forward_auth syntax, the URL patterns the authentik proxy provider covers, how long-lived connections behave, and whether the outpost runs under Docker or Kubernetes. Check each against the vendor documentation for the exact versions you run before you write any configuration.
Caddy version and directive syntax
The forward_auth directive, its uri and copy_headers options and its placement within a site block are the parts of the configuration most likely to change between Caddy releases. Confirm the syntax against the Caddy documentation for the exact version in your container image or package repository, not the version you saw in an example. Record the version in your deployment notes, because a Caddy upgrade is the most common reason a working route stops passing identity headers.
Proxy provider URL coverage
Confirm that the authentik proxy provider's external host and the paths you intend to protect match what the outpost will receive. Static assets, API paths, login callbacks and health checks may need separate handling, and some applications issue requests to alternate hostnames. Map every path pattern your application uses before you decide which routes go through forward auth and which bypass it.
WebSocket and streaming behavior
Applications that use WebSockets, server-sent events or long file uploads depend on the proxy keeping the connection open after the authentication check. Test these paths with your own client traffic through the full Caddy chain, including token expiry during a long session. Do not assume that a successful page load proves that a WebSocket upgrade or a streamed response passes through the outpost unchanged.
Docker or Kubernetes deployment
The outpost placement, its network path from Caddy and the way it is started and upgraded differ between a Docker host and a Kubernetes cluster. On Docker, confirm how Caddy resolves the outpost container name and which network both containers share. On Kubernetes, confirm the Service address and whether an ingress controller sits in front of Caddy. Check the authentik installation guide for your platform before you fix the network design.
Authentik version and outpost match
The outpost and the authentik server must run the same release line, or the proxy protocol between them can fail in ways that look like Caddy problems. Confirm which authentik versions are currently receiving security fixes and plan your upgrade order in advance. Check the security policy and release notes for your version before you choose a target.
TLS and trusted header handling
Caddy terminates TLS and forwards identity headers to the upstream application, so the application must only accept those headers from Caddy. Confirm that the application cannot be reached directly, that Caddy strips any client-supplied copies of the headers before setting its own, and that the cookie domain used by authentik matches the hostnames you protect. Test these controls with requests that try to forge the headers.
Questions teams ask before setting up authentik with Caddy
Does Caddy need a plugin to use authentik forward auth?
No. Caddy ships with the forward_auth directive in its standard build, so you do not need a third-party plugin to send requests to the authentik outpost before they reach your application. What you do need is a reachable outpost endpoint, a route that sends the outpost path to authentik, and a forward_auth block that copies the identity headers you want the application to read. Confirm the directive syntax and header names against the Caddy version you deploy, since the configuration is short and easy to get subtly wrong.
How long does a single-application setup with Caddy usually take?
The duration depends on how many applications are involved, how well the team already knows authentik, and whether the outpost runs in Docker, on Kubernetes or as a standalone service. A single application behind one Caddy site is typically a focused piece of work once the authentik provider, application and outpost are in place, with most of the time spent on testing logout, session behavior and header handling. No duration is fixed here, so ask any partner or your own team to break the estimate down by provider setup, Caddy configuration, testing and handover.
What drives the cost of setting up authentik forward auth with Caddy?
The main cost drivers are the number of applications and hostnames, whether each application gets its own provider or a shared parent domain is used, the state of the existing Caddy and DNS setup, and the hosting model for authentik and its outpost. Testing effort also grows with the number of applications that depend on identity headers, because each one needs to be checked for logout and access behavior. When you request a quote, ask for the scope as a list of applications, the environments covered, the testing approach and what documentation and handover you will receive.
Can our team set this up without outside help?
Yes, if you have people comfortable with reverse proxy configuration, DNS, TLS and authentik administration. The work spans three areas: authentik providers and applications, the outpost deployment, and the Caddy site configuration. Teams without recent experience in one of these areas often find that the header mapping and the outpost path cause the most rework. Many teams split the work, with an internal owner for the applications and a specialist review of the proxy configuration before production.
What does our team need to provide for the work?
You will need administrative access to the authentik instance and the Caddy hosts, a DNS zone you can change, a clear list of applications and their hostnames, and a named owner who can approve access policies. You will also need a test environment or a maintenance window if the application has no staging copy. Ask the partner to confirm in writing which systems they will access, through what accounts, and how changes are reviewed and rolled back.
Do we need authentik Enterprise to protect applications with Caddy?
No. The authentik pricing page lists an Open Source edition at no cost, and the proxy provider used for forward auth is part of the platform's provider types. The pricing page places Google Workspace and Microsoft Entra ID integrations, mTLS client certificate authentication, privileged access management, password history, enhanced audit logging and ticket-based support in the Enterprise edition, so check whether any of those features are required for your environment. The licensing for the source is also split: most code is MIT-licensed, while the enterprise directory has its own license. Confirm both points against the current pricing page and the repository before you commit to an edition.
What breaks most often in a Caddy forward auth setup?
The most common failures are an outpost path that Caddy does not route to authentik, identity headers that are missing or renamed when they reach the application, and cookies scoped to the wrong domain so that sign-in loops. TLS mismatches between Caddy and the outpost endpoint also cause confusing errors. Check the outpost path first, then confirm the headers in the application logs, then verify the cookie domain and the redirect URL. Ask your partner or internal owner to document these checks as part of the handover.
How DeployOpen scopes and delivers forward auth with Caddy
DeployOpen plans and builds the authentik outpost path, Caddy forward_auth block and header mapping for your applications inside your own infrastructure. Each engagement starts with a written scope that names the protected applications, the hostnames, the TLS approach and the verification steps before any configuration changes.
How we scope
- Inventory which applications sit behind Caddy and which ones can trust identity headers from the outpost.
- Agree in writing whether each application uses a single-application provider or a shared parent domain.
- Confirm the authentik version, the outpost deployment method and the target supported release line before work begins.
How we build
- Configure the outpost path, forward_auth block and copied identity headers in a Caddyfile reviewed under version control.
- Let Caddy handle TLS certificates for each protected hostname and confirm redirects complete without loops.
- Test sign-in, sign-out, unauthenticated access and header values for each application against the written scope.
How we hand over
- Deliver the Caddyfile, provider and application settings, and a record of each test case with its result.
- Document how to add an application, rotate the outpost token and roll back a Caddy configuration change.
- Walk your operations team through the runbook so they can change routes without redesigning access control.
How we support
- Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
- Ongoing support, upgrades and continuous testing are available as a retainer after handover.
- Ask any quote to itemize the outpost, Caddy, testing and documentation work separately so you can compare options.

