Integrating Authentik with NGINX via the auth_request directive and with Caddy via the forward_auth directive: building the outpost location, translating the X-authentik-* identity headers, redirecting logged-out users to the portal, and comparing all three proxies.

In episode 12, you installed the forwardAuth middleware in Traefik — the easiest integration because everything is declarative via labels. Episode 13 reviews two other proxies equally important in the self-hosted ecosystem: NGINX and Caddy. Both use the same forward authentication pattern — the proxy asks, the outpost answers, identity is forwarded — but with different mechanisms and configuration philosophies.
NGINX is the classic proxy controlled through text configuration blocks; Caddy is the modern proxy prioritizing Caddyfile simplicity and automatic HTTPS. Understanding all three keeps you vendor-agnostic: the conceptual pattern is the same, only the language differs.
The auth_request directive sends a sub-request to a location on every incoming request. Its behavior is strict:
The called location is /outpost.goauthentik.io/auth/nginx, and the entire /outpost.goauthentik.io path must be proxied to the outpost without authentication. The full configuration:
server {
listen 443 ssl;
server_name app.example.com;
location / {
proxy_pass http://app:8080;
proxy_set_header Host $host;
auth_request /outpost.goauthentik.io/auth/nginx;
auth_request_set $username $upstream_http_x_authentik_username;
auth_request_set $groups $upstream_http_x_authentik_groups;
auth_request_set $email $upstream_http_x_authentik_email;
auth_request_set $name $upstream_http_x_authentik_name;
auth_request_set $uid $upstream_http_x_authentik_uid;
proxy_set_header X-authentik-username $username;
proxy_set_header X-authentik-groups $groups;
proxy_set_header X-authentik-email $email;
proxy_set_header X-authentik-name $name;
proxy_set_header X-authentik-uid $uid;
error_page 401 = @goauthentik_proxy_signin;
}
location /outpost.goauthentik.io {
proxy_pass http://authentik-server:9000/outpost.goauthentik.io;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location @goauthentik_proxy_signin {
internal;
return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}
}Parts worth understanding:
auth_request /outpost.goauthentik.io/auth/nginx; — the core line sending a sub-request to the outpost location.auth_request_set — stores headers from the outpost response into NGINX variables. The outpost returns the user's identity via X-authentik-* headers, which we then forward to the application with proxy_set_header.X-Original-URL — tells the outpost the original URL the user requested. Without this header, path-based policy evaluation (episode 6) doesn't work correctly.proxy_pass_request_body off; — the verification sub-request doesn't need to carry the original request body.error_page 401 = @goauthentik_proxy_signin; — when the outpost answers 401, redirect to /outpost.goauthentik.io/start?rd=... which starts the login flow and then returns to the original page.Important
The X-authentik-* headers can be forged by a client if the application is directly reachable bypassing NGINX. Make sure the application is only exposed through the proxy, and let proxy_set_header overwrite values coming from the client. This is the core of Trusted Header SSO: only the proxy is allowed to set identity headers.
Caddy provides a built-in forward_auth directive. It does the same thing as NGINX's auth_request, but with a much shorter syntax. At the same time, every site block automatically gets a TLS certificate from Let's Encrypt — one thing you can fully delegate.
app.example.com {
route {
reverse_proxy /outpost.goauthentik.io/* http://authentik-server:9000
forward_auth http://authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid
}
reverse_proxy app:8080
}
}Let's break it down:
route — guarantees the directive execution order: the outpost path is proxied first, authentication runs, then the application.reverse_proxy /outpost.goauthentik.io/* http://authentik-server:9000 — forwards the entire outpost path to the outpost without authentication.forward_auth http://authentik-server:9000 — every other request is sent as a sub-request to the outpost.uri /outpost.goauthentik.io/auth/caddy — the Caddy-specific verification endpoint.copy_headers — copies identity headers from the outpost response to the application request. Casing matters here: headers are written X-Authentik-Username and so on, not X-authentik-username.By default, Caddy doesn't trust headers from other proxies and cleans potentially forged headers. If there's another proxy layer in front of Caddy, add the trusted_proxies directive inside the forward_auth block.
| Proxy | Mechanism | Outpost endpoint | Identity forwarded | Configuration |
|---|---|---|---|---|
| Traefik | forwardAuth middleware | /auth/traefik | authResponseHeaders | Dynamic config or Docker labels |
| NGINX | auth_request + auth_request_set | /auth/nginx | variables → proxy_set_header | Manual configuration blocks |
| Caddy | forward_auth + copy_headers | /auth/caddy | copy_headers | Concise Caddyfile, automatic HTTPS |
All three point to the same outpost and receive the same identity headers. The proxy choice should be based on what you already use in your infrastructure, not on the authentication features.
A calm test sequence for all three proxies:
X-authentik-username header — use a simple application that displays all request headers (for example traefik/whoami)./outpost.goauthentik.io/ping path responds 204 — this proves the outpost is reachable from the proxy.The most frequent problems:
auth_request_set (NGINX) or the copy_headers casing (Caddy).rd in the redirect, which is built from X-Original-URL.Validate the syntax before reloading: nginx -t for NGINX, and caddy validate --config Caddyfile for Caddy.
Tip
Start with one test application (like traefik/whoami) before protecting production applications. Separating authentication problems from application problems makes debugging much faster.
This episode closes the reverse proxy integration phase. You now master three ways to install the same pattern: Traefik's forwardAuth middleware with Docker labels, NGINX's auth_request with auth_request_set, and Caddy's forward_auth with copy_headers — plus three outpost endpoints, each using the suffixes /auth/traefik, /auth/nginx, and /auth/caddy.
The pattern you carry forward: the proxy always forwards the X-authentik-* headers, and authorization stays with Authentik through policies. In episode 14, we change phase: SAML provider configuration — making Authentik act as an Identity Provider for enterprise applications that don't yet support OIDC. See you there!