fix(supabase): use internal URL for all server-side clients with cookie name matching
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 5m38s
Workflow / ⚫️ Test (push) Has been skipped

ROOT CAUSE FIX: All server-side Supabase clients (server-client, middleware-client,
server-admin-client) now use SUPABASE_INTERNAL_URL (http://supabase-kong:8000)
when available, with cookieOptions.name set to match the external URL's cookie key
(e.g. sb-myeasycms-auth-token). This gives us:
- Reliable Docker-internal networking (no hairpin NAT through Traefik)
- Correct session cookie matching between browser and server
- No more 500 errors on SSR pages that query Supabase

Reverted per-page try/catch workarounds since root cause is now fixed.
This commit is contained in:
Zaid Marzguioui
2026-04-01 13:53:59 +02:00
parent 4aa11cd408
commit 49fd6b65b9
7 changed files with 64 additions and 29 deletions

View File

@@ -9,8 +9,8 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
/**
* Creates a middleware client for Supabase.
*
* @param {NextRequest} request - The Next.js request object.
* @param {NextResponse} response - The Next.js response object.
* Uses SUPABASE_INTERNAL_URL when available for reliable Docker networking,
* with cookieOptions.name matching the external URL's cookie key.
*/
export function createMiddlewareClient<GenericSchema = Database>(
request: NextRequest,
@@ -18,7 +18,15 @@ export function createMiddlewareClient<GenericSchema = Database>(
) {
const keys = getSupabaseClientKeys();
return createServerClient<GenericSchema>(keys.url, keys.publicKey, {
const internalUrl = process.env.SUPABASE_INTERNAL_URL;
const url = internalUrl || keys.url;
const cookieOptions = internalUrl
? { name: deriveCookieName(keys.url) }
: undefined;
return createServerClient<GenericSchema>(url, keys.publicKey, {
...(cookieOptions ? { cookieOptions } : {}),
cookies: {
getAll() {
return request.cookies.getAll();
@@ -35,3 +43,13 @@ export function createMiddlewareClient<GenericSchema = Database>(
},
});
}
function deriveCookieName(supabaseUrl: string): string {
try {
const hostname = new URL(supabaseUrl).hostname;
const ref = hostname.split('.')[0]!;
return `sb-${ref}-auth-token`;
} catch {
return 'sb-localhost-auth-token';
}
}

View File

@@ -15,7 +15,8 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
export function getSupabaseServerAdminClient<GenericSchema = Database>() {
warnServiceRoleKeyUsage();
const url = getSupabaseClientKeys().url;
const keys = getSupabaseClientKeys();
const url = process.env.SUPABASE_INTERNAL_URL || keys.url;
const secretKey = getSupabaseSecretKey();
return createClient<GenericSchema>(url, secretKey, {

View File

@@ -9,11 +9,27 @@ import { getSupabaseClientKeys } from '../get-supabase-client-keys';
/**
* @name getSupabaseServerClient
* @description Creates a Supabase client for use in the Server.
*
* In Docker deployments, the server can optionally use SUPABASE_INTERNAL_URL
* (e.g. http://supabase-kong:8000) for faster, more reliable connections.
* The cookieOptions.name is set to match the external URL's cookie name
* so session cookies from the browser are correctly read.
*/
export function getSupabaseServerClient<GenericSchema = Database>() {
const keys = getSupabaseClientKeys();
return createServerClient<GenericSchema>(keys.url, keys.publicKey, {
// Use internal URL for server-side requests if available
const internalUrl = process.env.SUPABASE_INTERNAL_URL;
const url = internalUrl || keys.url;
// When using an internal URL, we must set the cookie name to match
// the external URL's cookie key so the server can read browser cookies.
const cookieOptions = internalUrl
? { name: deriveCookieName(keys.url) }
: undefined;
return createServerClient<GenericSchema>(url, keys.publicKey, {
...(cookieOptions ? { cookieOptions } : {}),
cookies: {
async getAll() {
const cookieStore = await cookies();
@@ -36,3 +52,17 @@ export function getSupabaseServerClient<GenericSchema = Database>() {
},
});
}
/**
* Derives the Supabase cookie name from a URL, matching @supabase/ssr behavior.
* e.g. "https://myeasycms.frontieralgorithmics.de" → "sb-myeasycms-auth-token"
*/
function deriveCookieName(supabaseUrl: string): string {
try {
const hostname = new URL(supabaseUrl).hostname;
const ref = hostname.split('.')[0]!;
return `sb-${ref}-auth-token`;
} catch {
return 'sb-localhost-auth-token';
}
}