93818a2d2c
The deployment came up healthy, served a valid certificate, and returned 200 — and was completely unreachable. Two distinct causes, both invisible from inside the host: 1. Every other site on this proxy binds to a private VNIC address. Caddy groups site blocks into servers BY listen address, so a block without `bind` landed in a separate server on :443. The specific listener wins for traffic arriving on that address, which is all public traffic after NAT, so requests hit the server that had never heard of these hostnames and fell through to an empty 200. Testing from the host with --resolve 127.0.0.1 worked perfectly, which is exactly why this was worth chasing from a third machine instead of trusting a local check. 2. The CSP blocked the inline pre-paint theme script, so dark-mode users would have seen a white flash on every load. Fixed with the script's hash rather than 'unsafe-inline', which would have defeated the policy, and rather than an external file, which would have reintroduced the flash. Editing that script changes its hash and silently breaks it, so that is written down. Verified from an independent host: health returns JSON, the app serves, an unauthenticated API call is refused, the short alias redirects, security headers are present, and the existing sites on the proxy are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
38 lines
1.6 KiB
Caddyfile
38 lines
1.6 KiB
Caddyfile
# Caddy — reverse proxy for PIG.
|
|
#
|
|
# Serve the app and the API from ONE hostname. Auth sessions live in
|
|
# per-origin browser storage, so splitting them across two hostnames makes
|
|
# sign-in loop endlessly in a way that looks like a server fault.
|
|
|
|
primeintellectgrowth.com, www.primeintellectgrowth.com {
|
|
encode zstd gzip
|
|
|
|
# The MCP endpoint, when Streamable HTTP is enabled. Same origin as the
|
|
# app so it shares the session and needs no CORS allowance.
|
|
reverse_proxy 127.0.0.1:8920
|
|
|
|
|
|
# The inline script hash covers the pre-paint theme script in index.html,
|
|
# which sets light/dark before first paint so dark-mode users do not get a
|
|
# white flash. It cannot be an external file without reintroducing that
|
|
# flash, and it cannot use 'unsafe-inline' without defeating the CSP.
|
|
#
|
|
# IMPORTANT: editing that script changes its hash and CSP will silently
|
|
# block it. The browser console says exactly which hash it wants.
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
X-Content-Type-Options "nosniff"
|
|
X-Frame-Options "DENY"
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
# The app is entirely first-party except for the auth provider, which
|
|
# it must reach over XHR.
|
|
Content-Security-Policy "default-src 'self'; connect-src 'self' https://*.supabase.co; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'sha256-1tTDwCq+TCEyPDSZeYqW5HbmP+unUg8hrgRiZBiH/IU='; frame-ancestors 'none'; base-uri 'self'"
|
|
-Server
|
|
}
|
|
}
|
|
|
|
# Short alias. A redirect rather than a second origin, deliberately — see above.
|
|
pig.karti.ai {
|
|
redir https://primeintellectgrowth.com{uri} permanent
|
|
}
|