| 1 | # DropHawk - Caddyfile |
| 2 | # Caddy 2.11.4-alpine |
| 3 | # |
| 4 | # Listens on drophawk.lan (operator adds this to Pi-hole / AdGuard). |
| 5 | # Also matches localhost and the host IP for LAN access without DNS. |
| 6 | # TLS is automatic when using a real domain; for .lan names Caddy will |
| 7 | # fall back to self-signed if no ACME challenge is reachable - the |
| 8 | # operator should either add a DNS challenge or accept the self-signed cert. |
| 9 | # |
| 10 | # SSE note: flush_interval -1 disables response buffering so SSE streams |
| 11 | # reach the browser immediately. Critically, proxy_buffering is off by default |
| 12 | # in Caddy. |
| 13 | |
| 14 | { |
| 15 | # Global options |
| 16 | admin off |
| 17 | # Adjust log level for production |
| 18 | log { |
| 19 | level INFO |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | # ─── Main site (drophawk.lan + localhost fallback) ───────────────────────────── |
| 24 | |
| 25 | :80 { |
| 26 | |
| 27 | # ── Grafana (served under /grafana/) ────────────────────────────────────── |
| 28 | handle /grafana/* { |
| 29 | reverse_proxy grafana:3000 { |
| 30 | header_up Host {upstream_hostport} |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | # ── ntfy push notification server ───────────────────────────────────────── |
| 35 | # Available at /ntfy/* so the iOS app can subscribe over LAN. |
| 36 | handle /ntfy/* { |
| 37 | uri strip_prefix /ntfy |
| 38 | reverse_proxy ntfy:80 { |
| 39 | header_up Host {upstream_hostport} |
| 40 | header_up X-Forwarded-Prefix /ntfy |
| 41 | # ntfy requires host header for routing |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | # ── Backend API - SSE-safe ──────────────────────────────────────────────── |
| 46 | handle /api/* { |
| 47 | reverse_proxy backend:8080 { |
| 48 | header_up Host {upstream_hostport} |
| 49 | header_up X-Real-IP {remote_host} |
| 50 | header_up X-Forwarded-For {remote_host} |
| 51 | header_up X-Forwarded-Proto {scheme} |
| 52 | |
| 53 | # Disable buffering - critical for SSE (/api/stream) |
| 54 | flush_interval -1 |
| 55 | |
| 56 | # SSE connections can be long-lived; extend timeouts |
| 57 | transport http { |
| 58 | dial_timeout 5s |
| 59 | response_header_timeout 0 |
| 60 | read_timeout 0 |
| 61 | write_timeout 0 |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | # ── Frontend (Next.js) - everything else ───────────────────────────────── |
| 67 | handle { |
| 68 | reverse_proxy frontend:3000 { |
| 69 | header_up Host {upstream_hostport} |
| 70 | header_up X-Real-IP {remote_host} |
| 71 | header_up X-Forwarded-For {remote_host} |
| 72 | header_up X-Forwarded-Proto {scheme} |
| 73 | |
| 74 | # Next.js SSR pages: no buffering for streaming RSC |
| 75 | flush_interval -1 |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | # Security headers |
| 80 | header { |
| 81 | X-Frame-Options "SAMEORIGIN" |
| 82 | X-Content-Type-Options "nosniff" |
| 83 | Referrer-Policy "strict-origin-when-cross-origin" |
| 84 | -Server |
| 85 | } |
| 86 | |
| 87 | # Compress responses (brotli needs a custom Caddy build; gzip is built-in) |
| 88 | encode gzip |
| 89 | |
| 90 | # Access logs |
| 91 | log { |
| 92 | output stdout |
| 93 | format json |
| 94 | level INFO |
| 95 | } |
| 96 | } |