1. The problem I’m having:
Hi, so this is my first post, and have read the guidelines, but if I have missed something, please let me know and will happily learn ![]()
So I have only recently moved over to caddy and have successfully ported over my nginx proxy manager config into the caddyFile setup (to be honest it was easier than I thought and the system means I have ALOT less “advanced settings” enabled.. which is great. So far very happy.
I have hit my first bump in the road that I can’t seem to get around.. I am trying to proxy my router, as prefer to go to router.home.example.com rather than 192.168…
However it seems like I am stuck in a weird loop, So what happens:
I go to the proxy site, it loads as normal (checked F12 and also the logs (see below) and everything is returning fine.
When I enter my password (which I have checked against going directly to the router site), it 403’s me (again see below).. from my point of view I get no popup etc, I just get a reloaded page with no password entered.
I am completely stuck on what to do and why it is happening, and looking for some help if possible?
2. Error messages and/or full log output:
192.168.87.92 - - [27/Aug/2025:14:31:53 +0000] "GET / HTTP/3.0" 304 0
192.168.87.92 - - [27/Aug/2025:14:31:53 +0000] "GET /webpages/index.html HTTP/3.0" 304 0
192.168.87.92 - - [27/Aug/2025:14:31:53 +0000] "GET /webpages/config.json HTTP/3.0" 304 0
192.168.87.92 - - [27/Aug/2025:14:31:54 +0000] "POST /cgi-bin/luci/;stok=/locale?form=lang HTTP/3.0" 200 137
192.168.87.92 - - [27/Aug/2025:14:31:54 +0000] "POST /cgi-bin/luci/;stok=/locale?form=country HTTP/3.0" 200 40
192.168.87.92 - - [27/Aug/2025:14:31:54 +0000] "POST /cgi-bin/luci/;stok=/locale?form=list HTTP/3.0" 200 817
192.168.87.92 - - [27/Aug/2025:14:31:54 +0000] "POST /cgi-bin/luci/;stok=/device_config?form=config HTTP/3.0" 200 4496
192.168.87.92 - - [27/Aug/2025:14:31:55 +0000] "POST /cgi-bin/luci/;stok=/login?form=check_factory_default HTTP/3.0" 200 44
192.168.87.92 - - [27/Aug/2025:14:31:55 +0000] "POST /cgi-bin/luci/;stok=/device_config?form=config HTTP/3.0" 200 4496
192.168.87.92 - - [27/Aug/2025:14:31:55 +0000] "POST /cgi-bin/luci/;stok=/login?form=keys HTTP/3.0" 200 336
192.168.87.92 - - [27/Aug/2025:14:31:55 +0000] "POST /cgi-bin/luci/;stok=/login?form=sysmode HTTP/3.0" 200 57
192.168.87.92 - - [27/Aug/2025:14:31:56 +0000] "POST /cgi-bin/luci/;stok=/domain_login?form=dlogin HTTP/3.0" 200 182
192.168.87.92 - - [27/Aug/2025:14:34:11 +0000] "POST /cgi-bin/luci/;stok=/login?form=auth HTTP/3.0" 200 189
192.168.87.92 - - [27/Aug/2025:14:34:11 +0000] "POST /cgi-bin/luci/;stok=/login?form=login HTTP/3.0" 403 0
192.168.87.92 - - [27/Aug/2025:14:34:11 +0000] "GET / HTTP/3.0" 304 0
192.168.87.92 - - [27/Aug/2025:14:34:11 +0000] "GET /webpages/index.html HTTP/3.0" 304 0
192.168.87.92 - - [27/Aug/2025:14:34:11 +0000] "GET /webpages/config.json HTTP/3.0" 304 0
3. Caddy version:
v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=
4. How I installed and ran Caddy:
a. System environment:
Docker setup, using docker compose
b. Command:
To reload the caddyfile :
docker compose exec -w /etc/caddy caddy caddy reload
c. Service/unit/compose file:
caddy:
image: tailuk/caddy-server:latest
hostname: caddy
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
# logging:
# driver: "journald"
# options:
# tag: "caddy" # Optional: Add a tag to easily identify logs in journalctl
env_file: .env
volumes:
- ./caddy/conf:/etc/caddy
- ./caddy/site:/srv
- ./caddy/data:/data
- ./caddy/config:/config
- ./caddy/log:/log
networks:
proxy_network:
ipv4_address: 172.18.250.254 # Set this so that we have a standard route
d. My complete Caddy config:
# Global options block to enable the DNS challenge
{
email bob@bob.com # Optional but recommended
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
# Trust proxies for all reverse proxy directives in this Caddyfile
servers {
trusted_proxies static 172.18.0.0/16
}
log default {
output file /log/caddy_access.log {
roll_size 10mb
roll_keep 10
roll_keep_for 720h
}
}
}
(subdomain-log) {
log {
hostnames {args[0]}
format transform "{common_log}"
output file /log/access-{args[0]}.log {
roll_size 10mb
roll_keep 5
roll_keep_for 720h
}
}
}
# Certificate for *.example.com
*.example.com, [bathlab.casa](http://example.com) {
@audiobookshelf host audiobookshelf.example.com
import subdomain-log audiobookshelf.example.com
handle @audiobookshelf {
@autoLaunchblock {
query autoLaunch=*
not {
client_ip 192.168.87.0/24 172.18.0.0/16 10.11.13.0/24 100.64.0.0/24
}
}
# If the matcher is a hit, respond with a 403 Forbidden status
abort @autoLaunchblock
reverse_proxy audiobookshelf:80
}
@authentik host auth.example.com
import subdomain-log auth.example.com
handle @authentik {
reverse_proxy authentik-server:9000
}
@jitsi host jitsi.example.com
import subdomain-log jitsi.example.com
handle @jitsi {
# directive execution order is only as stated if enclosed with route.
route {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://authentik-server:9000 {
header_up Host {http.reverse_proxy.upstream.host}
}
# forward authentication to outpost
forward_auth http://authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
# optional, in this config trust all private ranges, should probably be set to the outposts IP
#trusted_proxies 192.168.87.0/24 172.18.0.0/16 10.11.13.0/24 100.64.0.0/24 100.122.75.96 100.66.255.64 100.92.202.16
}
reverse_proxy jitsi-web:80
}
}
handle {
# Unhandled domains fall through to here,
# but we don't want to accept their requests
respond 404
}
}
# Certificate for *.home.bathlab.casa
*.home.bathlab.casa {
@allowed_ips client_ip 192.168.87.0/24 172.18.0.0/16 10.11.13.0/24 100.64.0.0/24 100.122.75.96 100.66.255.64 100.92.202.16
# Matchers for allowed IP addresses/subnets
# Block all other IPs
@blocked_ips {
not {
client_ip 192.168.87.0/24 172.18.0.0/16 10.11.13.0/24 100.64.0.0/24 100.122.75.96 100.66.255.64 100.92.202.16
}
}
handle @blocked_ips {
# Unhandled domains fall through to here,
# but we don't want to accept their requests
respond 404
}
# Matcher for a home-network service
@adguard host adguard.home.bathlab.casa
import subdomain-log adguard.home.bathlab.casa
handle @adguard {
reverse_proxy 192.168.87.22:24248
}
@dashboard host dash.home.example.com
handle @dashboard {
reverse_proxy homarr:7575
}
@frigate host frigate.home.example.com
handle @frigate {
# directive execution order is only as stated if enclosed with route.
route {
# always forward outpost path to actual outpost
reverse_proxy /outpost.goauthentik.io/* http://authentik-server:9000 {
header_up Host {http.reverse_proxy.upstream.host}
}
# forward authentication to outpost
forward_auth http://authentik-server:9000 {
uri /outpost.goauthentik.io/auth/caddy
# capitalization of the headers is important, otherwise they will be empty
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
# optional, in this config trust all private ranges, should probably be set to the outposts IP
#trusted_proxies 192.168.87.0/24 172.18.0.0/16 10.11.13.0/24 100.64.0.0/24 100.122.75.96 100.66.255.64 100.92.202.16
}
reverse_proxy frigate:5000
}
}
@portainer_i5 host portainer-i5.home.example.com
handle @portainer_i5 {
reverse_proxy portainer:9000
}
@radarr host radarr.home.example.com
handle @radarr {
reverse_proxy radarr:7878
}
@readarr host readarr.home.example.com
handle @readarr {
reverse_proxy readarr:8787
}
@router host router.home.example.com
import subdomain-log router.home.example.com
handle @router {
reverse_proxy 192.168.87.1:80
}
handle {
# Unhandled domains fall through to here,
# but we don't want to accept their requests
respond 404
}
}
5. Links to relevant resources:
I have no links directly. I hvae looked through the caddy docs online : The Caddyfile — Caddy Documentation
As well as searched reddit + this help forum.. but I can’t find anything that seems to reference the experience I am getting on this one site.
FYI - I know it says don’t redact anything. I have done a replace of the servername to example.com - as don’t feel comfortable sharing that.