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 [...] |
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 |
Set these based on which auth modes your app uses:
| Variable | Required when |
|---|---|
SUPABASE_URL |
Always |
SUPABASE_SECRET_KEY |
allow: 'secret' or using supabaseAdmin |
SUPABASE_PUBLISHABLE_KEY |
allow: 'public' |
SUPABASE_JWKS |
allow: 'user' (JWT verification) |
.env exampleSUPABASE_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({ allow: 'public:web' }, handler)
// Accept any secret key
withSupabase({ allow: '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 allow: 'public' (which looks for the "default" key) works with both forms.
When both singular and plural forms are set, the plural form takes priority.
SUPABASE_JWKS accepts two formats:
# Standard JWKS format
SUPABASE_JWKS={"keys":[{"kty":"RSA","n":"...","e":"AQAB"}]}
# Bare array (convenience)
SUPABASE_JWKS=[{"kty":"RSA","n":"...","e":"AQAB"}]
When SUPABASE_JWKS is not set, JWT verification (allow: 'user') is unavailable.
The SDK reads environment variables using this priority:
Deno.env.get(name) — Deno (including Supabase Edge Functions)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:
Enable node-compat in wrangler.toml:
compatibility_flags = ["nodejs_compat"]
Pass overrides via the env config option:
withSupabase(
{
allow: '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(
{
allow: '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>
jwks: JsonWebKeySet | null
}
Malformed JSON in environment variables doesn't throw — the SDK falls back to empty values:
SUPABASE_PUBLISHABLE_KEYS or SUPABASE_SECRET_KEYS → empty {}SUPABASE_JWKS → null (JWT verification unavailable)SUPABASE_URL → EnvError (this is the only hard requirement)