fix(csp): allow self-hosted Supabase Realtime WebSocket in connect-src (#954)

connect-src listed the https Supabase origin plus wss://*.supabase.co,
but never the wss variant of a self-hosted Supabase URL. Supabase
Realtime opens wss://<host>/realtime/v1/websocket, which CSP blocked;
WebKit throws synchronously on a CSP-blocked new WebSocket(), so Safari
unmounted the dashboard into the error boundary (Chromium only logs).

- next.config.ts: add supabaseWsUrl (NEXT_PUBLIC_SUPABASE_WS_URL, or
  the Supabase URL with https to wss / http to ws) to connect-src
- Dockerfile: bake a __NEXT_PUBLIC_SUPABASE_WS_URL__ sentinel, since
  the CSP is fixed at build time and only sed-substituted at runtime
- docker-entrypoint.sh: derive the wss origin from
  NEXT_PUBLIC_SUPABASE_URL unless overridden, substitute the sentinel
- .env.docker.example: document the optional override

Hosted is unaffected: the wss form of *.supabase.co was already
allowlisted, so the added token is redundant there.

Fixes #893

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-09 21:10:13 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 982fe77f72
commit 15e5dc1a01
4 changed files with 37 additions and 1 deletions
+12
View File
@@ -75,7 +75,18 @@ if [ -n "$SUBST_PATHS" ]; then
v=${v//|/\\|}
printf %s "$v"
}
# Realtime WebSocket origin for the CSP (issue #893): derive from the
# Supabase URL unless explicitly overridden. https:// becomes wss://;
# http:// becomes ws:// for plain-HTTP local installs. Without this token
# in connect-src, Supabase Realtime's WebSocket is CSP-blocked on
# self-hosted installs and WebKit crashes the dashboard.
if [ -z "${NEXT_PUBLIC_SUPABASE_WS_URL:-}" ]; then
NEXT_PUBLIC_SUPABASE_WS_URL=$(printf %s "$NEXT_PUBLIC_SUPABASE_URL" \
| sed -e 's|^https://|wss://|' -e 's|^http://|ws://|')
fi
E_SUPABASE_URL=$(sed_esc "$NEXT_PUBLIC_SUPABASE_URL")
E_SUPABASE_WS_URL=$(sed_esc "$NEXT_PUBLIC_SUPABASE_WS_URL")
E_SUPABASE_ANON_KEY=$(sed_esc "$NEXT_PUBLIC_SUPABASE_ANON_KEY")
E_APP_URL=$(sed_esc "$NEXT_PUBLIC_APP_URL")
E_VAPID_PUBLIC_KEY=$(sed_esc "${NEXT_PUBLIC_VAPID_PUBLIC_KEY:-}")
@@ -96,6 +107,7 @@ if [ -n "$SUBST_PATHS" ]; then
| tr '\n' '\0' \
| xargs -0 -r sed -i \
-e "s|__NEXT_PUBLIC_SUPABASE_URL__|${E_SUPABASE_URL}|g" \
-e "s|__NEXT_PUBLIC_SUPABASE_WS_URL__|${E_SUPABASE_WS_URL}|g" \
-e "s|__NEXT_PUBLIC_SUPABASE_ANON_KEY__|${E_SUPABASE_ANON_KEY}|g" \
-e "s|__NEXT_PUBLIC_APP_URL__|${E_APP_URL}|g" \
-e "s|__NEXT_PUBLIC_VAPID_PUBLIC_KEY__|${E_VAPID_PUBLIC_KEY}|g" \