# How to Fix Supabase Sign Up Not Working

- Tool: Supabase
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Supabase (all plans), @supabase/supabase-js v2+
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

```
const { data, error } = await supabase.auth.signUp({
  email: 'test@example.com',
  password: 'securepassword123',
})

// Log the full response for debugging
console.log('User:', data.user)
console.log('Session:', data.session)
console.log('Error:', error)

// Common scenarios:
// 1. user exists, session is null → Email confirmation is enabled, check inbox
// 2. user is null, error exists → Check error.message
// 3. user exists, session exists → Signup succeeded, email confirmation is disabled

// Common error messages:
// 'User already registered' → Email is taken
// 'Password should be at least 6 characters' → Password too short
// 'Signups not allowed for this instance' → Signups are disabled
// 'Email rate limit exceeded' → Too many emails sent
```

**Expected result:** The console output reveals whether the issue is an error, missing session (email confirmation), or something else.

### 2. 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.

```
// Verify these match Dashboard > Settings > API
const supabase = createClient(
  'https://your-project-ref.supabase.co',  // Must match exactly
  'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' // Anon key, NOT service role key
)

// Common mistakes:
// - Using 'http://' instead of 'https://'
// - Trailing slash on the URL
// - Using the service role key (starts with 'eyJ' but different value)
// - Using keys from a different Supabase project
// - Environment variables not loaded (undefined values)
```

**Expected result:** The Supabase URL and anon key match exactly what is shown in the Dashboard.

### 3. 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.

```
# Check these Dashboard settings:

# 1. Dashboard > Authentication > Providers > Email
#    - Is 'Confirm email' enabled? If yes, users must click the email link
#    - For testing, you can temporarily disable it

# 2. Dashboard > Authentication > Settings > SMTP
#    - Default SMTP: 2 emails/hour limit
#    - Configure custom SMTP (Resend, SendGrid, Mailgun) for production

# 3. Dashboard > Authentication > URL Configuration
#    - Site URL: your production URL (e.g., https://myapp.com)
#    - Redirect URLs: add localhost for development
#      e.g., http://localhost:3000/auth/callback

# Quick test: disable 'Confirm email' temporarily
# If signup works with it disabled, the issue is email delivery
```

**Expected result:** Email confirmation settings are verified and SMTP is configured to handle your signup volume.

### 4. 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.

```
// Dashboard > Authentication > URL Configuration
// Site URL: https://myapp.com
// Redirect URLs:
//   - http://localhost:3000/auth/callback
//   - https://myapp.com/auth/callback
//   - https://myapp.vercel.app/auth/callback

// In your signUp call, specify the redirect:
const { data, error } = await supabase.auth.signUp({
  email: 'test@example.com',
  password: 'securepassword123',
  options: {
    emailRedirectTo: 'http://localhost:3000/auth/callback',
  },
})

// The callback page should handle the auth token exchange:
// This is needed for PKCE flow used by SSR apps
// See: how-to-set-up-supabase-auth-with-next-js
```

**Expected result:** Redirect URLs are configured for both development and production environments.

### 5. 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.

```
-- Check if your trigger function has the correct security mode
-- Security definer bypasses RLS (needed for triggers that insert into other tables)
create or replace function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = ''
as $$
begin
  insert into public.profiles (id, email)
  values (new.id, new.email);
  return new;
end;
$$;

-- The trigger should be on auth.users
create trigger on_auth_user_created
  after insert on auth.users
  for each row execute function public.handle_new_user();

-- If using security invoker (default), you need an INSERT policy:
create policy "Allow profile creation"
on profiles for insert
to authenticated
with check (auth.uid() = id);

-- Check if the trigger exists:
-- Dashboard > Database > Triggers
-- Or run in SQL Editor:
select * from information_schema.triggers
where trigger_name = 'on_auth_user_created';
```

**Expected result:** The trigger function creates profile rows successfully when new users sign up.

## Complete code example

File: `debug-signup.ts`

```typescript
// Complete signup debugging script
// Run this in your browser console or a test file

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY

// Step 1: Verify environment variables are loaded
console.log('Supabase URL:', supabaseUrl ? 'SET' : 'MISSING')
console.log('Supabase Key:', supabaseKey ? 'SET' : 'MISSING')

if (!supabaseUrl || !supabaseKey) {
  throw new Error('Missing Supabase environment variables')
}

const supabase = createClient(supabaseUrl, supabaseKey)

async function debugSignup() {
  const testEmail = `test-${Date.now()}@example.com`
  const testPassword = 'testpassword123'

  console.log('Testing signup with:', testEmail)

  // Step 2: Attempt signup
  const { data, error } = await supabase.auth.signUp({
    email: testEmail,
    password: testPassword,
  })

  if (error) {
    console.error('Signup error:', error.message)
    console.error('Error status:', error.status)

    if (error.message.includes('rate limit')) {
      console.log('FIX: Configure custom SMTP in Dashboard > Auth > Settings')
    } else if (error.message.includes('not allowed')) {
      console.log('FIX: Enable signups in Dashboard > Auth > Providers > Email')
    } else if (error.message.includes('already registered')) {
      console.log('FIX: This email is already in use')
    }
    return
  }

  // Step 3: Check session status
  if (data.session) {
    console.log('Signup successful with immediate session')
    console.log('Email confirmation is DISABLED')
  } else if (data.user) {
    console.log('Signup successful but no session')
    console.log('Email confirmation is ENABLED — check inbox')
    console.log('User ID:', data.user.id)
    console.log('Email confirmed:', data.user.email_confirmed_at ? 'Yes' : 'No')
  }

  // Step 4: Check if profile was created (if using trigger)
  if (data.user) {
    const { data: profile, error: profileError } = await supabase
      .from('profiles')
      .select('*')
      .eq('id', data.user.id)
      .single()

    if (profileError) {
      console.warn('Profile not created:', profileError.message)
      console.log('Check trigger function and RLS policies on profiles table')
    } else {
      console.log('Profile created successfully:', profile)
    }
  }
}

debugSignup()
```

## Common mistakes

- **Assuming signup failed because session is null, when actually email confirmation is enabled and working correctly** — undefined Fix: 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.
- **Hitting the default SMTP 2 emails/hour rate limit during development testing** — undefined Fix: Configure a custom SMTP provider (Resend, SendGrid, Mailgun) in Dashboard > Authentication > Settings > SMTP. The free tier of Resend allows 100 emails/day.
- **Not adding localhost to the redirect URLs list, causing confirmation links to fail in development** — undefined Fix: 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

## 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.

---

Source: https://www.rapidevelopers.com/supabase-tutorial/how-to-fix-supabase-sign-up-not-working
© RapidDev — https://www.rapidevelopers.com/supabase-tutorial/how-to-fix-supabase-sign-up-not-working
