Critique my (working) caddyfile?

Hi all! I fell in love with caddy when I started getting seriously into selfhosting about a year ago. I’ve thought many times about switching to NGINX since a lot of selfhosting docs provide explicit instructions for it, but the powerful simplicity of caddy keeps me here.

There are a few things I’m still figuring out, but given I don’t want to migrate, I feel like I should invest in expanding my caddy knowledge and take advantage of the community where documentation might be more sparse.

1. The problem I’m having:

In honesty, my Caddyfile is currently working, albeit cobbled together. I don’t want to waste anyone’s time, but before I start making posts about adding features to my setup, I want to make sure I’m going about my current configuration the most efficient way that’s easiest to build on. I’d love if folks could give me tips on things to change/add/optimize.

Specifically, later on, I’m hoping to:

  • Insert some custom HTML into matched URLs (e.g. banners to provide service updates, or extra usage instructions on a specific app page all my users all have trouble with).
  • Monitor logs for intrusions with something like Fail2Ban, hence the common log transformer you’ll see. I haven’t gotten that going yet.
  • Lock things down a bit more—I don’t think I’m taking full advantage of security features, like blocking requests that don’t come through Cloudflare.
    • Or maybe migrate to Cloudflare Tunnels completely?? Thoughts??

2. Error messages and/or full log output:

N/A

3. Caddy version:

2.7.6

4. How I installed and ran Caddy:

ASUS AX86U router running AsusMerlin firmware, using the tutorial I wrote.

Compiled with xcaddy to include the Cloudflare and log transformer plugins.

My service diagram is basically:

  • Cloudflare (unproxied CNAMEs)
  • to Afraid dynDNS
  • to Caddy on my router
  • to services (mostly running on Docker)

(plus a few Cloudflare tunnels, as a treat)

a. System environment:

uname -a:

Linux {hostname} 4.1.52 #2 SMP PREEMPT {date} aarch64 ASUSWRT-Merlin

b. Command:

From the init.d script:

caddy start --config /opt/etc/caddy/Caddyfile

c. Service/unit/compose file:

/opt/etc/init.d/S98caddy:

#!/bin/sh

ENABLED="yes"
PROCS="caddy"
ARGS="start --config /opt/etc/caddy/Caddyfile"
WORK_DIR="/opt/share/caddy"
DESC=$PROCS
PREARGS=""
PRECMD=""
POSTCMD=""

d. My complete Caddy config:

{
	storage file_system {
		root /opt/share/caddy
	}
	# Why'd I ever put this in here??? Seriously it's caused me so much pain
	#	auto_https disable_redirects 

	servers {
		trusted_proxies static 10.0.0.0/25
	}
}
adguardhome:80 {
	reverse_proxy 10.0.0.1:14711
}
homeassistant:80 {
	reverse_proxy 10.0.0.10:8123
}
# Note to forum folks: I forgot why I put the cors stuff in here—
# I think because I was having issues with Organizr 
# (server dashboard) iframe'ing HTTP tabs through the HTTPS connection
(cors) {
	@origin{args.0} header Origin {args.0}
	header @origin{args.0} Access-Control-Allow-Origin "{args.0}"
}
*.alchemy.lgbt, alchemy.lgbt {
	tls {
		dns cloudflare nB0S2hAqKPmEXv457T0iKLww10Jwvn8g_vDLTcUo
	}
	log {
		output file /opt/var/log/caddy/alchemy-lgbt-access.log
		format transform "{common_log}"
	}
	handle /meetings* {
		redir https://powers.notion.site/Meetings-bd30dec237e4471eb1a816050a93cb7e?pvs=4
	}
	handle /equipment* {
		redir https://powers.notion.site/aaf804a8b7a24db2a66b60af509c8900?v=542610798236418ebb5a59a0696876b4&pvs=4
	}
	handle /weather* {
		redir https://weather.com/weather/tenday/l/4b278ae41ffbc63dc108baa22ab29eaed39fad1daf03fdda0349e1e31f1285e8136cf0ab2d0e6cb208d67603c663c07e
	}
	handle /stuff* {
		redir https://docs.google.com/spreadsheets/d/1iFaKwf26t2xtfmMGuGNASyh8nCktyh4jz96cuyZtnM8/edit?usp=sharing&_sm_nck=1
	}
	handle /people* {
		redir https://powers.notion.site/080fc268d9a04645a8df95deda179bf5?v=19e38629dd9c4fedb8fce6856e5ecf81&pvs=4
	}
	handle /tasks* {
		redir https://powers.notion.site/5ed4fa70351e423cac2ecf46ba14ede4?v=87805f8cce7f49c590983aba45be4fec&pvs=4
	}
	handle /data* {
		redir https://powers.notion.site/Data-0e758ab8f65a4b3a90007c7b4df964d0?pvs=4
	}
	handle {
		redir https://powers.notion.site/Wish-U-Were-Queer-1d83c29d1c7c49d4b4221cd325a9d68f?pvs=4
	}
}
*.badkitty.zone {
	tls {
		dns cloudflare nB0S2hAqKPmEXv457T0iKLww10Jwvn8g_vDLTcUo
	}
	log {
		output file /opt/var/log/caddy/star-badkitty-zone-access.log
		format transform "{common_log}"
	}
	@start host start.badkitty.zone
	handle @start {
		redir https://powers.notion.site/Bad-Kitty-Media-Server-ef82bfe8d71a405692fefc10a036861a?pvs=4
	}
	@pebbles host pebbles.badkitty.zone
	handle @pebbles {
		redir https://badkitty.mmm.page/pebbles
	}
	@hass host hass.badkitty.zone
	handle @hass {
		reverse_proxy 10.0.0.10:8123
	}
	@plex host plex.badkitty.zone
	handle @plex {
		reverse_proxy 10.0.0.100:32400
	}
	@watch host watch.badkitty.zone
	handle @watch {
		import cors *
		reverse_proxy 10.0.0.100:8088
	}
	@print host print.badkitty.zone
	handle @print {
		reverse_proxy 10.0.0.100:9191
	}
	@home host home.badkitty.zone
	handle @home {
		reverse_proxy 10.0.0.100:8088
	}
	@tv host tv.badkitty.zone
	handle @tv {
		reverse_proxy 10.0.0.100:8989
	}
	@stats host stats.badkitty.zone
	handle @stats {
		reverse_proxy 10.0.0.100:8181
	}
	@movies host movies.badkitty.zone
	handle @movies {
		reverse_proxy 10.0.0.100:7878
	}
	@trackers host trackers.badkitty.zone
	handle @trackers {
		reverse_proxy 10.0.0.100:9696
	}
	@jellyfin host jellyfin.badkitty.zone
	handle @jellyfin {
		reverse_proxy 10.0.0.100:8096
	}
	@justwatch host justwatch.badkitty.zone
	handle @justwatch {
		reverse_proxy 10.0.0.100:8096
	}
	@alchemy host alchemy.badkitty.zone
	handle @alchemy {
		redir https://powers.notion.site/Wish-U-Were-Queer-1d83c29d1c7c49d4b4221cd325a9d68f?pvs=4
	}
}
*.powe.rs {
	tls {
		dns cloudflare nB0S2hAqKPmEXv457T0iKLww10Jwvn8g_vDLTcUo
	}
	log {
		output file /opt/var/log/caddy/powers-access.log
		format transform "{common_log}"
	}
	@docs host docs.powe.rs
	handle @docs {
		reverse_proxy 10.0.0.100:7215
	}
	@auth host auth.powe.rs
	handle @auth {
		reverse_proxy 10.0.0.100:7214
	}
	@s3 host s3.powe.rs
	handle @s3 {
		reverse_proxy 10.0.0.100:7216
	}
}
*.x.powe.rs, x.powe.rs {
	tls {
		dns cloudflare nB0S2hAqKPmEXv457T0iKLww10Jwvn8g_vDLTcUo
	}

	log {
		output file /opt/var/log/caddy/x-powers-access.log
		format transform "{common_log}"
	}
	@agh host adguard.x.powe.rs
	handle @agh {
		reverse_proxy 10.0.0.1:14711
	}

	@hass host hass.x.powe.rs
	handle @hass {
		reverse_proxy 10.0.0.10:8123
	}

	@movies host movies.x.powe.rs
	handle @movies {
		reverse_proxy 10.0.0.100:7878
	}

	@torrents host torrents.x.powe.rs
	handle @torrents {
		reverse_proxy 10.0.0.100:55555
	}

	@start host start.x.powe.rs
	handle @start {
		reverse_proxy 10.0.0.100:5690
	}

	@transmission host transmission.x.powe.rs
	handle @transmission {
		reverse_proxy 10.0.0.100:9091
	}

	@plex host plex.x.powe.rs
	handle @plex {
		reverse_proxy 10.0.0.100:32400
	}

	@subtitles host subtitles.x.powe.rs
	handle @subtitles {
		reverse_proxy 10.0.0.100:6767
	}

	@home host home.x.powe.rs
	handle @home {
		reverse_proxy 10.0.0.100:8088
	}

	@downloads host downloads.x.powe.rs
	handle @downloads {
		reverse_proxy http://localhost:8080
	}
	@trackers host trackers.x.powe.rs
	handle @trackers {
		reverse_proxy 10.0.0.100:9696
	}
	@tv host tv.x.powe.rs
	handle @tv {
		reverse_proxy 10.0.0.100:8989
	}

	@watch host watch.x.powe.rs
	handle @watch {
		reverse_proxy 10.0.0.100:5055
	}

	@x host x.powe.rs
	handle @x {
		root * /opt/share/www
		file_server
	}
	# Fallback for otherwise unhandled domains
	handle {
		root * /opt/share/www
		file_server
	}
}

Thanks for your time!! <3

No worries! It’s valid.

Easier said than done. Every website is different.

You could use GitHub - caddyserver/replace-response: Caddy module that performs replacements in response bodies but it would certainly be a rabbithole.

If you’re behind Cloudflare, you’ll probably want to configure trusted_proxies Global options (Caddyfile) — Caddy Documentation with Cloudflare’s IPs. There’s a cloudflare IP source plugin you can use to avoid needing to insert the IP addresses in your config directly.

There’s also a matcher plugin which allows you to use IP source plugins (the same one as for trusted_proxied) instead of the remote_ip matcher as per that wiki post you linked to.

Certainly an option. Many users prefer that. Up to you.

I hope that’s not your real Cloudflare API key. That must remain secret. If it’s real, you need to revoke the API key right now and refresh it. If it’s real, someone could use it to take control of your domain.

You can avoid making that mistake by using env vars for secrets like that, so they don’t appear literally in your config.

Aside from that, your config looks fine. It’s just a bunch of simple reverse_proxy and redir, not much to speak to.

1 Like

UGH—I carefully edited out my key, then wound up repasting my Caddyfile for some reason and forgot to do it again. Thank you so much. It’s already been revoked/replaced.

And thank you for the rest of your advice!

To your knowledge (or anyone else’s reading), is going the handle route for all my subdomains effective compared to some other method? I rarely see it used in caddy examples I see in the wild or on the forum.

It’s the pattern we recommend if you must use a wildcard cert. See the docs:

For your site block with the redirects, you don’t need to use handle for that, you could apply your matchers right to the redir directives, since that’s the only thing you’re doing. For example:

redir /meetings* https://...
redir /equipment* https://...
redir https://... # fallback
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.