When Supabase sign up is not working, the most common causes are the default SMTP email rate limit (2 emails per hour), email confirmation being enabled without proper redirect URL configuration, missing or incorrect API keys, and RLS policies blocking profile creation triggers. Work through the checklist: verify API keys, check email confirmation settings, configure custom SMTP, and ensure your redirect URLs are correct in Dashboard > Authentication > URL Configuration.
Systematic Debugging Checklist for Supabase Signup Failures
Supabase sign up failures are frustrating because they often fail silently — the signUp() method may return a user object without a session, return an error, or appear to succeed while the confirmation email never arrives. This tutorial provides a systematic checklist to identify and fix every common cause, from the 2 emails/hour SMTP limit to misconfigured RLS policies on profile tables.
Prerequisites
- A Supabase project with Authentication enabled
- Access to the Supabase Dashboard
- Your frontend code calling supabase.auth.signUp()
- A test email address for debugging
Step-by-step guide
Check the signUp response for error details
Check the signUp response for error details
Start by examining the exact response from supabase.auth.signUp(). The response contains a user object, a session object, and an error object. If session is null but user exists, email confirmation is enabled and the user needs to verify their email. If error is not null, the error message tells you exactly what went wrong. Log the full response to understand the state.
1const { data, error } = await supabase.auth.signUp({2 email: 'test@example.com',3 password: 'securepassword123',4})56// Log the full response for debugging7console.log('User:', data.user)8console.log('Session:', data.session)9console.log('Error:', error)1011// Common scenarios:12// 1. user exists, session is null → Email confirmation is enabled, check inbox13// 2. user is null, error exists → Check error.message14// 3. user exists, session exists → Signup succeeded, email confirmation is disabled1516// Common error messages:17// 'User already registered' → Email is taken18// 'Password should be at least 6 characters' → Password too short19// 'Signups not allowed for this instance' → Signups are disabled20// 'Email rate limit exceeded' → Too many emails sentExpected result: The console output reveals whether the issue is an error, missing session (email confirmation), or something else.
Verify your Supabase URL and anon key
Verify your Supabase URL and anon key
Incorrect API keys are a common cause of signup failures. Go to Dashboard > Settings > API and verify that the URL and anon key in your code match exactly. The anon key (not the service role key) should be used in frontend code. Check for trailing spaces, incorrect project references, or accidentally using keys from a different project.
1// Verify these match Dashboard > Settings > API2const supabase = createClient(3 'https://your-project-ref.supabase.co', // Must match exactly4 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // Anon key, NOT service role key5)67// Common mistakes:8// - Using 'http://' instead of 'https://'9// - Trailing slash on the URL10// - Using the service role key (starts with 'eyJ' but different value)11// - Using keys from a different Supabase project12// - Environment variables not loaded (undefined values)Expected result: The Supabase URL and anon key match exactly what is shown in the Dashboard.
Check email confirmation settings and SMTP limits
Check email confirmation settings and SMTP limits
By default, Supabase enables email confirmation for hosted projects. When enabled, signUp() returns a user with a null session, and a confirmation email is sent. The default SMTP has a 2 emails per hour limit — if you have been testing signups, you may have hit this limit. Check Dashboard > Authentication > Providers > Email to see if 'Confirm email' is enabled, and check Dashboard > Authentication > Settings for SMTP configuration.
1# Check these Dashboard settings:23# 1. Dashboard > Authentication > Providers > Email4# - Is 'Confirm email' enabled? If yes, users must click the email link5# - For testing, you can temporarily disable it67# 2. Dashboard > Authentication > Settings > SMTP8# - Default SMTP: 2 emails/hour limit9# - Configure custom SMTP (Resend, SendGrid, Mailgun) for production1011# 3. Dashboard > Authentication > URL Configuration12# - Site URL: your production URL (e.g., https://myapp.com)13# - Redirect URLs: add localhost for development14# e.g., http://localhost:3000/auth/callback1516# Quick test: disable 'Confirm email' temporarily17# If signup works with it disabled, the issue is email deliveryExpected result: Email confirmation settings are verified and SMTP is configured to handle your signup volume.
Verify redirect URLs are configured
Verify redirect URLs are configured
If email confirmation is enabled, the confirmation email contains a link that redirects to your application. If the redirect URL is not configured in the Supabase Dashboard, the confirmation flow will fail. Go to Dashboard > Authentication > URL Configuration and add both your production URL and localhost development URL to the Redirect URLs list.
1// Dashboard > Authentication > URL Configuration2// Site URL: https://myapp.com3// Redirect URLs:4// - http://localhost:3000/auth/callback5// - https://myapp.com/auth/callback6// - https://myapp.vercel.app/auth/callback78// In your signUp call, specify the redirect:9const { data, error } = await supabase.auth.signUp({10 email: 'test@example.com',11 password: 'securepassword123',12 options: {13 emailRedirectTo: 'http://localhost:3000/auth/callback',14 },15})1617// The callback page should handle the auth token exchange:18// This is needed for PKCE flow used by SSR apps19// See: how-to-set-up-supabase-auth-with-next-jsExpected result: Redirect URLs are configured for both development and production environments.
Check for RLS issues on profile tables and triggers
Check for RLS issues on profile tables and triggers
Many Supabase apps use a trigger to automatically create a profile row when a new user signs up. If the trigger function fails (often due to RLS policies on the profiles table), the signup itself may appear to succeed but downstream operations fail. Check Dashboard > Database > Functions for your trigger function and ensure RLS allows the trigger to insert into the profiles table.
1-- Check if your trigger function has the correct security mode2-- Security definer bypasses RLS (needed for triggers that insert into other tables)3create or replace function public.handle_new_user()4returns trigger5language plpgsql6security definer set search_path = ''7as $$8begin9 insert into public.profiles (id, email)10 values (new.id, new.email);11 return new;12end;13$$;1415-- The trigger should be on auth.users16create trigger on_auth_user_created17 after insert on auth.users18 for each row execute function public.handle_new_user();1920-- If using security invoker (default), you need an INSERT policy:21create policy "Allow profile creation"22on profiles for insert23to authenticated24with check (auth.uid() = id);2526-- Check if the trigger exists:27-- Dashboard > Database > Triggers28-- Or run in SQL Editor:29select * from information_schema.triggers30where trigger_name = 'on_auth_user_created';Expected result: The trigger function creates profile rows successfully when new users sign up.
Complete working example
1// Complete signup debugging script2// Run this in your browser console or a test file34import { createClient } from '@supabase/supabase-js'56const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL7const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY89// Step 1: Verify environment variables are loaded10console.log('Supabase URL:', supabaseUrl ? 'SET' : 'MISSING')11console.log('Supabase Key:', supabaseKey ? 'SET' : 'MISSING')1213if (!supabaseUrl || !supabaseKey) {14 throw new Error('Missing Supabase environment variables')15}1617const supabase = createClient(supabaseUrl, supabaseKey)1819async function debugSignup() {20 const testEmail = `test-${Date.now()}@example.com`21 const testPassword = 'testpassword123'2223 console.log('Testing signup with:', testEmail)2425 // Step 2: Attempt signup26 const { data, error } = await supabase.auth.signUp({27 email: testEmail,28 password: testPassword,29 })3031 if (error) {32 console.error('Signup error:', error.message)33 console.error('Error status:', error.status)3435 if (error.message.includes('rate limit')) {36 console.log('FIX: Configure custom SMTP in Dashboard > Auth > Settings')37 } else if (error.message.includes('not allowed')) {38 console.log('FIX: Enable signups in Dashboard > Auth > Providers > Email')39 } else if (error.message.includes('already registered')) {40 console.log('FIX: This email is already in use')41 }42 return43 }4445 // Step 3: Check session status46 if (data.session) {47 console.log('Signup successful with immediate session')48 console.log('Email confirmation is DISABLED')49 } else if (data.user) {50 console.log('Signup successful but no session')51 console.log('Email confirmation is ENABLED — check inbox')52 console.log('User ID:', data.user.id)53 console.log('Email confirmed:', data.user.email_confirmed_at ? 'Yes' : 'No')54 }5556 // Step 4: Check if profile was created (if using trigger)57 if (data.user) {58 const { data: profile, error: profileError } = await supabase59 .from('profiles')60 .select('*')61 .eq('id', data.user.id)62 .single()6364 if (profileError) {65 console.warn('Profile not created:', profileError.message)66 console.log('Check trigger function and RLS policies on profiles table')67 } else {68 console.log('Profile created successfully:', profile)69 }70 }71}7273debugSignup()Common mistakes when fixing Supabase Sign Up Not Working
Why it's a problem: Assuming signup failed because session is null, when actually email confirmation is enabled and working correctly
How to avoid: When email confirmation is enabled, signUp() returns a user with session: null. This is expected behavior. The user must click the confirmation link before a session is created.
Why it's a problem: Hitting the default SMTP 2 emails/hour rate limit during development testing
How to avoid: Configure a custom SMTP provider (Resend, SendGrid, Mailgun) in Dashboard > Authentication > Settings > SMTP. The free tier of Resend allows 100 emails/day.
Why it's a problem: Not adding localhost to the redirect URLs list, causing confirmation links to fail in development
How to avoid: Add http://localhost:3000 (or your dev port) to Dashboard > Authentication > URL Configuration > Redirect URLs.
Best practices
- Always destructure and check both data and error from supabase.auth.signUp() responses
- Configure a custom SMTP provider before starting development to avoid the 2 emails/hour limit
- Add all development and production URLs to the redirect URLs list in Dashboard > Authentication > URL Configuration
- Use security definer on trigger functions that create profiles on signup to avoid RLS permission issues
- Test signup with a unique email each time (use timestamp-based emails) to avoid 'already registered' errors
- Check the Supabase Auth logs (Dashboard > Logs > Auth) for server-side error details when client-side errors are vague
- Temporarily disable email confirmation during early development for faster iteration, then re-enable for production
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
My Supabase signup is not working. The signUp() call returns a user object but session is null, and I never receive the confirmation email. Walk me through a debugging checklist including SMTP settings, email confirmation configuration, redirect URLs, and how to check the auth logs for errors.
Debug my Supabase signup flow. The signUp function returns no error but the user is not being created. Show me how to check the auth logs in Dashboard, verify the API keys are correct, check if email confirmation is blocking the flow, and confirm that my profiles trigger function is working.
Frequently asked questions
Why does signUp return a user but session is null?
This means email confirmation is enabled. The user record is created but the session is not issued until the user clicks the confirmation link in their email. Check Dashboard > Authentication > Providers > Email to see the 'Confirm email' setting.
Why am I not receiving the confirmation email?
The most common cause is the default SMTP rate limit of 2 emails per hour. If you have been testing signups, you may have exhausted this limit. Configure a custom SMTP provider in Dashboard > Authentication > Settings to fix this.
Can I disable email confirmation for development?
Yes. Go to Dashboard > Authentication > Providers > Email and uncheck 'Confirm email'. This gives you an immediate session on signup. Remember to re-enable it for production.
Why do I get 'Signups not allowed for this instance'?
This means public signups are disabled. Go to Dashboard > Authentication > Settings and check that 'Allow new users to sign up' is enabled. This may have been disabled if the project was set to invite-only.
Why does my profile trigger fail after signup?
The most common cause is that the trigger function uses security invoker (default) and there is no INSERT policy on the profiles table. Use security definer on the trigger function or create an INSERT policy that allows the new user to create their profile row.
How do I check if signups are being rate limited?
Check Dashboard > Logs > Auth for rate limit errors. The error message will say 'Email rate limit exceeded'. The default limit is 2 emails per hour. Configure custom SMTP to raise this limit.
Can RapidDev help fix signup issues in my Supabase app?
Yes. RapidDev can diagnose and fix authentication flows including signup, email confirmation, SMTP configuration, and trigger functions for profile creation in your Supabase project.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation