@supabase/server - v1.7.0
    Preparing search index...

    On Supabase Platform and Local Development (CLI), all variables are auto-provisioned — no configuration needed

    Variable Format Description Available in
    SUPABASE_URL https://<ref>.supabase.co Your Supabase project URL All
    SUPABASE_PUBLISHABLE_KEYS {"default":"sb_publishable_..."} Named publishable keys as JSON object All
    SUPABASE_SECRET_KEYS {"default":"sb_secret_..."} Named secret keys as JSON object All
    SUPABASE_JWKS {"keys":[...]} or [...] Inline JSON Web Key Set for JWT verification All
    SUPABASE_PUBLISHABLE_KEY sb_publishable_... Single publishable key (fallback) Self-hosted, if manually exported
    SUPABASE_SECRET_KEY sb_secret_... Single secret key (fallback) Self-hosted, if manually exported
    SUPABASE_PUBLIC_URL https://<ref>.supabase.co Externally-visible URL of the Supabase stack. Preferred origin for OAuth protected resource metadata Self-hosted
    SUPABASE_FUNCTION_SLUG my-function The running function's slug. Yields a canonical /functions/v1/{slug} resource identifier with no path parsing Edge Functions
    SB_EXECUTION_ID UUID Set by the Edge Functions runtime for every invocation. Marks the Edge Functions environment for withOAuthProtectedResource's request-derived defaults Edge Functions

    Set these based on which auth modes your app uses:

    Variable Required when
    SUPABASE_URL Always. Also the last-resort authorizationServer for withOAuthProtectedResource
    SUPABASE_SECRET_KEY auth: 'secret', or when the handler accesses supabaseAdmin
    SUPABASE_PUBLISHABLE_KEY auth: 'publishable'
    SUPABASE_JWKS or SUPABASE_JWKS_URL auth: 'user' (JWT verification)
    SUPABASE_URL=https://.supabase.co
    SUPABASE_SECRET_KEY=sb_secret_...
    SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
    SUPABASE_JWKS={"keys":[...]}
    

    The SDK checks the plural form first (SUPABASE_PUBLISHABLE_KEYS), then falls back to the singular form (SUPABASE_PUBLISHABLE_KEY). The same applies to secret keys.

    Use this when you have multiple keys for different clients (web, mobile, internal):

    SUPABASE_PUBLISHABLE_KEYS={"default":"sb_publishable_default_abc","web":"sb_publishable_web_xyz","mobile":"sb_publishable_mobile_123"}
    SUPABASE_SECRET_KEYS={"default":"sb_secret_default_abc","internal":"sb_secret_internal_xyz"}

    You can then validate against specific keys with named key syntax:

    // Only accept the "web" publishable key
    withSupabase({ auth: 'publishable:web' }, handler)

    // Accept any secret key
    withSupabase({ auth: 'secret:*' }, handler)
    SUPABASE_PUBLISHABLE_KEY=sb_publishable_default_abc
    SUPABASE_SECRET_KEY=sb_secret_default_abc

    This is equivalent to setting the plural form with a single "default" entry:

    # These two are the same:
    SUPABASE_PUBLISHABLE_KEY=sb_publishable_default_abc
    SUPABASE_PUBLISHABLE_KEYS={"default":"sb_publishable_default_abc"}

    The singular form is a convenience for the common case where you only have one key. The SDK stores it internally as { default: "<value>" }, so auth: 'publishable' (which looks for the "default" key) works with both forms.

    When both singular and plural forms are set, the plural form takes priority.

    JWT verification (auth: 'user') needs a JWKS. For a Supabase project nothing extra is needed: when neither variable below is set, the JWKS URL is derived from the project URL as {url}/auth/v1/.well-known/jwks.json, which is where every project publishes its signing keys. The project URL is SUPABASE_PUBLIC_URL when set, otherwise SUPABASE_URL (or the url passed to resolveEnv()), the same order the OAuth issuer uses. Self-hosted stacks need SUPABASE_PUBLIC_URL: the compose file sets SUPABASE_URL to the Docker-internal gateway (http://kong:8000), which fails the transport rule below. SUPABASE_PUBLIC_URL must also be reachable from where this server runs; the compose default of http://localhost:8000 passes the loopback rule but points at the server's own container rather than the gateway. Set one of the two variables to override the derivation, for example to pin keys inline or to point at a non-Supabase issuer:

    # Inline JSONstandard JWKS format
    SUPABASE_JWKS={"keys":[{"kty":"RSA","n":"...","e":"AQAB"}]}

    # Inline JSONbare array (convenience, wrapped as { keys: [...] })
    SUPABASE_JWKS=[{"kty":"RSA","n":"...","e":"AQAB"}]

    # Remote JWKS endpointkeys are fetched on demand and cached in memory.
    # HTTPS is required for any non-loopback host; plain http:// is rejected
    # (a MITM on the JWKS fetch could swap in an attacker-controlled key and
    # forge JWTs that verify). http:// is allowed for loopback hosts only —
    # `localhost`, `127.0.0.0/8`, `::1`to support the local Supabase CLI.
    SUPABASE_JWKS_URL=https://<ref>.supabase.co/auth/v1/.well-known/jwks.json

    # Local development against `supabase start`:
    SUPABASE_JWKS_URL=http://localhost:54321/auth/v1/.well-known/jwks.json
    1. SUPABASE_JWKS — when set, treated as authoritative inline JSON.
    2. SUPABASE_JWKS_URL — only checked when SUPABASE_JWKS is unset or empty. Must be https://, except loopback hosts may use http://.
    3. Derived from SUPABASE_PUBLIC_URL{SUPABASE_PUBLIC_URL}/auth/v1/.well-known/jwks.json. Same transport rule as SUPABASE_JWKS_URL; when set, SUPABASE_URL is not consulted.
    4. Derived from SUPABASE_URL (or the url passed to resolveEnv()) — {SUPABASE_URL}/auth/v1/.well-known/jwks.json. Same transport rule, so a Docker-internal http://kong:8000 yields null.
    5. Otherwise — null. JWT verification (auth: 'user') is unavailable.

    The SDK reads environment variables using this priority:

    1. Deno.env.get(name) — Deno (including Supabase Edge Functions)
    2. process.env[name] — Node.js, Bun, Cloudflare Workers (with node-compat)

    Environment variables are auto-provisioned by the platform. Nothing to configure.

    Set variables via .env files (with a loader like dotenv for Node.js) or your deployment platform's environment configuration.

    Cloudflare Workers don't expose Deno.env or process.env by default. Two options:

    1. Enable node-compat in wrangler.toml:

      compatibility_flags = ["nodejs_compat"]
      
    2. Pass overrides via the env config option:

      withSupabase(
      {
      auth: 'user',
      env: {
      url: env.SUPABASE_URL,
      publishableKeys: { default: env.SUPABASE_PUBLISHABLE_KEY },
      secretKeys: { default: env.SUPABASE_SECRET_KEY },
      },
      },
      handler,
      )

    The env option on withSupabase, createSupabaseContext, and core primitives lets you override auto-detected values. Partial overrides are merged with what's resolved from environment variables:

    import { withSupabase } from '@supabase/server'

    export default {
    fetch: withSupabase(
    {
    auth: 'user',
    env: {
    url: 'http://localhost:54321', // override just the URL
    },
    },
    handler,
    ),
    }

    For manual environment resolution — useful in tests, custom setups, or debugging:

    import { resolveEnv } from '@supabase/server/core'

    const { data: env, error } = resolveEnv()
    if (error) {
    console.error(`Missing config: ${error.message}`)
    }

    // With overrides
    const { data: envOverridden } = resolveEnv({
    url: 'http://localhost:54321',
    publishableKeys: { default: 'test-key' },
    })

    resolveEnv returns a SupabaseEnv object:

    interface SupabaseEnv {
    url: string
    publishableKeys: Record<string, string>
    secretKeys: Record<string, string>
    // `URL` when SUPABASE_JWKS is a remote endpoint, `JsonWebKeySet` for inline keys
    jwks: JsonWebKeySet | URL | null
    }

    Malformed JSON in environment variables doesn't throw — the SDK falls back to empty values:

    • Malformed SUPABASE_PUBLISHABLE_KEYS or SUPABASE_SECRET_KEYS → empty {}
    • Malformed SUPABASE_JWKSnull (JWT verification unavailable)
    • Missing SUPABASE_URLEnvError (this is the only hard requirement)