keepcalculator

06 Numbers and Powers

Supabase Sessions Vanish on Vercel: Cookie & Config

Card No. 06·8098 · Updated · Standard formula, shown below

If your Supabase auth sessions disappear on Vercel, the cause is almost always a misconfigured cookie or an incorrect auth callback URL. Fixing it means ensuring your cookie storage uses the same site context across server and client, and that your redirect URLs match your deployed domain exactly. Below is a detailed walkthrough of the cookie callback and config settings that cause this issue.

Why Sessions Vanish: The Cookie and Callback Chain

Supabase Auth issues a session as a JWT (JSON Web Token) and stores it in a cookie. On Vercel, your app runs in two environments: server-side (Node.js or Edge Functions) and client-side (browser). If the cookie is set with a path, domain, or SameSite attribute that doesn’t match between these contexts, the browser may drop it or the server may not see it. The callback URL is the endpoint Supabase redirects to after a user signs in. If that URL doesn’t match your Vercel domain exactly (including trailing slashes or www vs non-www), the session token is never exchanged for a full session, and the user appears logged out.

How Supabase Auth Stores Sessions

When a user signs in, Supabase returns an access token, a refresh token, and a user object. In a typical web app, you store these in a cookie. The default Supabase client library (supabase-js) uses the browser’s localStorage by default, but on Vercel, you often need server-side rendering, which requires cookies. The cookie must be set with the following attributes:

  • Name: typically sb-access-token and sb-refresh-token (or a single sb-auth-token).
  • Path: / so it’s sent to all routes.
  • HttpOnly: recommended for security, but if you need to read it client-side, you’ll need it false.
  • SameSite: Lax is common for most apps; Strict can break OAuth redirects; None requires Secure.
  • Secure: must be true in production (HTTPS).
  • Domain: omit it or set it to your exact domain (e.g., example.com not www.example.com).

If any of these attributes are inconsistent between the server and client, the cookie may not be sent with requests, or the session may be lost on page reload.

Common Vercel + Supabase Configuration Pitfalls

Here are the most frequent mistakes that cause sessions to vanish:

  • Wrong callback URL in Supabase Dashboard: The redirect URL must be exactly https://your-app.vercel.app/auth/callback (or your custom domain). If you have a trailing slash or a different path, the OAuth flow fails.
  • Using localhost in production: If your callback is set to http://localhost:3000, it will only work locally. Vercel preview deployments need their own callback URLs.
  • Not handling the callback route: You need a route like /auth/callback that exchanges the authorization code for a session. If this route doesn’t exist or doesn’t set the cookie, the session is lost.
  • Mixing client and server Supabase instances: If you create a Supabase client on the server with a different cookie name than the one used on the client, the session won’t be found.
  • Forgetting to refresh the session: Access tokens expire after an hour. If you don’t refresh them using the refresh token, the session vanishes when the token expires.

Step-by-Step: Trace the Cookie and Callback

To diagnose the issue, follow these steps:

  1. Check your Supabase Dashboard: Go to Authentication → URL Configuration. Ensure the Site URL matches your Vercel domain (e.g., https://your-app.vercel.app). Add any redirect URLs you use (like https://your-app.vercel.app/auth/callback).
  2. Inspect the callback route: In your Next.js app, find the /auth/callback route. It should call supabase.auth.exchangeCodeForSession(code) and then store the session in a cookie. If you’re using the @supabase/ssr package, it handles this automatically.
  3. Verify cookie settings: In your middleware or server code, create the Supabase client with cookies. For example, in Next.js App Router, you’d use cookies() from next/headers. Ensure the cookie options are consistent: same name, path, and SameSite.
  4. Test with a browser’s dev tools: After signing in, open the Application tab → Cookies. Look for your Supabase cookies. If they’re not there, the callback didn’t set them. If they’re there but disappear on reload, the server isn’t reading them.
  5. Check server logs: On Vercel, view the function logs for errors like AuthSessionMissingError or Invalid JWT. These indicate the cookie isn’t being sent or parsed correctly.
  6. Verify environment variables: Ensure NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY are set in Vercel’s environment variables, and that they match the values in your local .env.local.

Fixing the Cookie Callback in Next.js (App Router)

If you’re using Next.js App Router, the recommended approach is to use @supabase/ssr to create a server client that reads cookies. Here’s a minimal example:

// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export function createClient() {
  const cookieStore = cookies()
  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() { return cookieStore.getAll() },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options))
          } catch {
            // Called from a Server Component, ignore
          }
        }
      }
    }
  )
}

And in your callback route:

// app/auth/callback/route.ts
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  const { searchParams, origin } = new URL(request.url)
  const code = searchParams.get('code')
  if (code) {
    const supabase = createClient()
    const { error } = await supabase.auth.exchangeCodeForSession(code)
    if (!error) {
      return NextResponse.redirect(`${origin}/`)
    }
  }
  return NextResponse.redirect(`${origin}/auth/error`)
}

This setup ensures the cookie is set and read consistently. If you’re not using App Router, the principle is the same: read cookies from the request, set cookies on the response.

Alternatives: Client-Only vs. Server-Side Sessions

If you’re still struggling, consider these alternatives:

  • Client-only auth: Store the session in localStorage (default supabase-js behavior). This works but doesn’t allow server-side rendering of user-specific data. You’ll need to handle loading states.
  • Use a different auth provider: Some developers switch to NextAuth.js (now Auth.js) with Supabase as a database adapter. This handles sessions via JWT in cookies and may be easier to configure.
  • Use Vercel’s Edge Middleware: You can refresh the session in middleware to keep it alive. This requires careful cookie handling but can prevent session loss on page navigation.

Frequently Asked Questions

Why does my session work locally but not on Vercel?

Localhost and your Vercel domain are different origins. The callback URL must be added to Supabase’s allowed redirects for each environment. Also, cookies set on localhost (http) may not be secure, while Vercel uses https, so the Secure flag must be set correctly.

What is the correct SameSite attribute for Supabase cookies?

For most apps, Lax is sufficient. It allows the cookie to be sent on top-level navigations (like OAuth redirects) but not on cross-site subrequests. Strict can break OAuth, and None requires Secure and may be blocked by browsers if not set properly.

How do I set up multiple Vercel preview deployments with Supabase?

Each preview deployment has a unique URL like your-app-xyz.vercel.app. You need to add each URL to Supabase’s redirect list, or use a wildcard pattern if Supabase supports it. Alternatively, you can use a single staging environment and disable previews for auth testing.

Why does my session vanish after a page refresh?

This usually means the cookie is not being sent with the server request. Check that the cookie is set with a path of / and that your server client is reading it correctly. Also ensure you’re not setting the cookie only on the client side (e.g., in a useEffect).

Can I use Supabase auth without a callback URL?

Yes, for email/password sign-in, you can use the signInWithPassword method, which returns a session directly without a redirect. However, for OAuth (Google, GitHub, etc.), a callback URL is required to exchange the authorization code.

What if my callback URL is correct but I still get an error?

Check the Vercel function logs for the exact error. Common issues include the code being expired (it lasts only a few minutes), the code being used more than once, or a mismatch between the code verifier and the code challenge (if using PKCE). Ensure your Supabase client uses the same PKCE settings in both the login and callback.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →