Need help with forward_auth directive

1. The problem I’m having:

I have multiple VPS servers (let’s call them Server A and Server B). I’m using an authentication middleware with forward_auth to enable both username/password and OAuth-based login. This setup works flawlessly on Server A for a site like app1.{$DOMAIN}, using the Caddyfile configuration below.
However, when setting up the same on Server B, I encountered an issue. In the tinyauth_forwarder snippet, I replaced the local address tinyauth:3000 with the external FQDN auth.{$DOMAIN} so that Server B can communicate with the authentication service running on Server A.
The problem is that Caddy on Server B doesn’t seem to trust the forwarded headers. When making API requests to the tinyauth service, the original X-Forwarded-* headers from the actual app are being overwritten by those from the reverse proxy instead.
From what I understand, this could be resolved by configuring trusted proxies in Caddy to ensure it properly preserves the forwarding headers. However, I haven’t been able to get that working. I’ve attached the relevant Caddyfile configuration and documentation links for reference.

2. Caddy version:

2.10.0

3. How I installed and ran Caddy:

OS - Ubuntu 24.10, arm64
Caddy is running inside Docker with below Caddyfile

My complete Caddy config:

Server A

(tinyauth_forwarder) {
	forward_auth tinyauth:3000 {
		uri /api/auth/caddy
	}
}

auth.{$DOMAIN} {
    reverse_proxy tinyauth:3000
}

app1.{$DOMAIN} {
    reverse_proxy app1:9898
    import tinyauth_forwarder *
}

Server B

(tinyauth_forwarder) {
	forward_auth auth.{$DOMAIN} {
		uri /api/auth/caddy
	}
}

app2.{$DOMAIN} {
    reverse_proxy app2:8080
    import tinyauth_forwarder *
}

4. Links to relevant resources:

Could you clarify this a bit? I’m getting a little lost with “the actual app” and “the reverse proxy”. Just trying to understand which one you’re referring to in each case.

Server A (working perfectly):

  1. Client → app1.{$DOMAIN} (Caddy)
  2. Caddy forward_auth → tinyauth:3000 (local container)
  3. If auth succeeds → Caddy → app1:9898

Server B (problematic):

  1. Client → app2.{$DOMAIN} (Caddy on Server B)
  2. Caddy forward_auth → auth.{$DOMAIN} (tinyauth reverse proxy on Server A)
  3. If auth succeeds → Caddy → app2:8080

When Server B’s Caddy makes the forward_auth request to auth.{$DOMAIN}, it’s going through Server A’s Caddy reverse proxy. This creates a chain:

Server B CaddyServer A Caddytinyauth container

Server A’s Caddy is overwriting the X-Forwarded-* headers that Server B sent, so tinyauth sees Server A’s IP instead of the original client’s information. I need to ensure that Server A preserves the original headers

I hope that clears it out properly

Thanks for the detailed description of your setup!

You might need to tweak your TinyAuth configuration a bit

I’m guessing it might be something like this?

entryPoints:
  web:
    forwardedHeaders:
      trustedIPs:
        - ServerB_IP_Address

Also, you may need to add this to Server A’s Caddyfile:

{
	servers {
		trusted_proxies static ServerB_IP_Address
	}
}

Thanks for the heads-up. I’ve added the following configuration to Server A’s Caddyfile. As for the other change you mentioned regarding entrypoints—that’s actually specific to Traefik (another reverse proxy), so no changes are needed within TinyAuth itself. Instead, we just need to replicate that configuration in Caddy.

Server A

{
	servers {
		trusted_proxies static ServerB_IP_Address
	}
}

(tinyauth_forwarder) {
	forward_auth tinyauth:3000 {
		uri /api/auth/caddy
	}
}

auth.{$DOMAIN} {
    reverse_proxy tinyauth:3000
}

app1.{$DOMAIN} {
    reverse_proxy app1:9898
    import tinyauth_forwarder *
}

Server B

(tinyauth_forwarder) {
	forward_auth https://auth.{$DOMAIN} {
		uri /api/auth/caddy
	}
}

app2.{$DOMAIN} {
    reverse_proxy app2:8080
    import tinyauth_forwarder *
}

When I visit app2.{$DOMAIN} in browser, it simply loads directly without getting redirected to tinyauth just like before. Here’s the curl -vL output of the same with my actual subdomain.
app1.{$DOMAIN} from Server A correctly redirects to TinyAuth for authentication, then loads the app after login.