# 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 ```bash 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 ```bash 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: ```bash systemctl restart tor cat /var/lib/tor/bflr/hostname ``` ## 3. Configure Caddy Edit `/etc/caddy/Caddyfile`: ```caddy # ─── 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: ```bash nano /home/bflr/buildfor_life_repair/.env ``` ``` HOST=127.0.0.1 PORT=3000 ``` Update systemd if HOST is set there too: ```bash nano /etc/systemd/system/bflr.service ``` ```ini Environment=HOST=127.0.0.1 ``` ## 5. Firewall Only Caddy needs external access: ```bash # 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 ```bash systemctl daemon-reload systemctl restart bflr systemctl enable --now caddy systemctl restart tor ``` ## 7. Verify ```bash # 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 ```bash # 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 ```