Files
buildfor_life_repair/docs/caddy-reverse-proxy.md
T
grabowski e6c213f35c
Deploy to LXC / deploy (push) Successful in 20s
Add Caddy reverse proxy guide for internal, Tor, and Yggdrasil access
Caddyfile config with shared proxy snippet that sets Host header for
CSRF compatibility. Handles:
- Public domain with auto HTTPS (Let's Encrypt)
- LAN/internal on port 80
- Tor hidden service via localhost:8880
- Yggdrasil IPv6 on port 80

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 14:02:01 +07:00

4.2 KiB

Caddy Reverse Proxy Setup

Caddy sits in front of the SvelteKit app and handles all access methods:

  • Internal network (LAN/Tailscale)
  • Tor hidden service
  • Yggdrasil mesh network
  • Public domain with automatic HTTPS

1. Install Caddy

apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update
apt install caddy

2. Install Tor

apt install tor

Edit /etc/tor/torrc:

HiddenServiceDir /var/lib/tor/bflr/
HiddenServicePort 80 127.0.0.1:8880

Restart and get your .onion address:

systemctl restart tor
cat /var/lib/tor/bflr/hostname

3. Configure Caddy

Edit /etc/caddy/Caddyfile:

# ─── Shared config ───────────────────────────────────────────────────

(proxy) {
	reverse_proxy 127.0.0.1:3000 {
		header_up Host collection.newedge.house
		header_up X-Real-IP {remote_host}
		header_up X-Forwarded-For {remote_host}
		header_up X-Forwarded-Proto {scheme}
	}
}

(common) {
	encode gzip
	request_body {
		max_size 50MB
	}
}

# ─── Public domain (automatic HTTPS via Let's Encrypt) ───────────────

collection.newedge.house {
	import common
	reverse_proxy 127.0.0.1:3000 {
		header_up X-Real-IP {remote_host}
		header_up X-Forwarded-For {remote_host}
		header_up X-Forwarded-Proto https
	}
}

# ─── Internal / LAN access (HTTP on port 80) ─────────────────────────

:80 {
	import common
	import proxy
}

# ─── Tor hidden service (HTTP on port 8880, Tor connects here) ───────

:8880 {
	import common
	import proxy
	bind 127.0.0.1
}

# ─── Yggdrasil (HTTP on port 80, bind to Yggdrasil IPv6) ─────────────
# Replace with your actual Yggdrasil address from: yggdrasilctl getSelf

http://[200:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:80 {
	import common
	import proxy
}

Important: Replace 200:xxxx:... with your actual Yggdrasil IPv6 address.

The (proxy) snippet sets Host: collection.newedge.house on all non-public routes so SvelteKit's CSRF check passes regardless of how you access the app.

4. Update the SvelteKit service

Change the app to only listen on localhost since Caddy handles external access:

nano /home/bflr/buildfor_life_repair/.env
HOST=127.0.0.1
PORT=3000

Update systemd if HOST is set there too:

nano /etc/systemd/system/bflr.service
Environment=HOST=127.0.0.1

5. Firewall

Only Caddy needs external access:

# Allow HTTP/HTTPS from anywhere
ufw allow 80/tcp
ufw allow 443/tcp

# Block direct app access from outside
ufw deny 3000/tcp

6. Start everything

systemctl daemon-reload
systemctl restart bflr
systemctl enable --now caddy
systemctl restart tor

7. Verify

# Public domain
curl -s -o /dev/null -w "%{http_code}" https://collection.newedge.house/login

# Internal
curl -s -o /dev/null -w "%{http_code}" http://localhost/login

# Tor (from Tor Browser)
# http://your-onion-address.onion/login

# Yggdrasil (from a Yggdrasil peer)
# http://[200:xxxx:...]/login

How it works

Internet ──► collection.newedge.house:443 ──► Caddy ──► :3000 (app)
LAN      ──► server-ip:80                 ──► Caddy ──► :3000 (app)
Tor      ──► .onion:80 ──► tor ──► :8880  ──► Caddy ──► :3000 (app)
Yggdrasil──► [200:...]:80                 ──► Caddy ──► :3000 (app)

All routes set Host: collection.newedge.house so SvelteKit CSRF passes. Caddy handles TLS for the public domain automatically via Let's Encrypt.

Troubleshooting

# Check Caddy status
systemctl status caddy
journalctl -u caddy -f

# Check Tor status
systemctl status tor
cat /var/lib/tor/bflr/hostname

# Check Yggdrasil address
yggdrasilctl getSelf

# Test reverse proxy
curl -H "Host: collection.newedge.house" http://127.0.0.1:3000/login