Redirect URLs and Site URL Configuration for Self-Hosted Supabase

Master redirect URL configuration for self-hosted Supabase. Fix email confirmation, OAuth callbacks, and magic link redirect issues.

Cover Image for Redirect URLs and Site URL Configuration for Self-Hosted Supabase

If you've ever clicked an email confirmation link from your self-hosted Supabase instance only to land on http://kong/auth/v1/verify instead of your actual domain, you're not alone. Redirect URL misconfiguration is one of the most common—and frustrating—issues in self-hosted Supabase deployments.

This guide covers everything you need to know about configuring Site URL, redirect URLs, and email template URLs for your self-hosted Supabase instance.

Understanding Supabase URL Configuration

Supabase uses several URL-related environment variables that work together to handle authentication flows. When any of these are misconfigured, you'll encounter broken email confirmations, OAuth callbacks that redirect nowhere, and magic links that fail silently.

There are three critical URL concepts:

  1. Site URL: The default redirect destination after authentication
  2. Redirect URLs: Allowed destinations for redirectTo parameters
  3. API External URL: The public-facing URL of your Supabase API

On Supabase Cloud, these are configured through the dashboard. On self-hosted instances, you configure them through environment variables in your docker-compose.yml file—and there's no UI to help you.

The Environment Variables That Matter

Here are the key environment variables for URL configuration:

# docker-compose.yml - auth service section
services:
  auth:
    environment:
      # Your public Supabase API URL
      API_EXTERNAL_URL: https://api.yourdomain.com
      
      # Default redirect after auth (your frontend)
      GOTRUE_SITE_URL: https://app.yourdomain.com
      
      # Comma-separated list of allowed redirect URLs
      GOTRUE_URI_ALLOW_LIST: https://app.yourdomain.com/*,https://staging.yourdomain.com/*
      
      # External URL for email links (usually same as API_EXTERNAL_URL)
      GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify

Each variable serves a specific purpose:

  • API_EXTERNAL_URL: The publicly accessible URL for your Supabase API. This is what gets embedded in email confirmation links.
  • GOTRUE_SITE_URL: Where users land after completing authentication when no redirectTo is specified.
  • GOTRUE_URI_ALLOW_LIST: Security allowlist for redirect destinations. Any redirectTo parameter must match a pattern here.

Fixing the Kong Redirect Problem

The most common issue—email links redirecting to http://kong/... instead of your domain—happens when API_EXTERNAL_URL isn't set correctly.

Inside Docker Compose, services communicate using container names. Kong is the API gateway container, so internal URLs reference kong. But email recipients aren't inside your Docker network. They need the external URL.

The fix is straightforward:

# Wrong - uses internal Docker network name
API_EXTERNAL_URL: http://kong:8000

# Correct - uses your public domain
API_EXTERNAL_URL: https://api.yourdomain.com

If you're running without a custom domain during development, use your server's IP:

API_EXTERNAL_URL: http://YOUR_SERVER_IP:8000

After changing this, restart the auth container:

docker compose restart auth

Configuring OAuth Redirect URIs

OAuth providers like Google, GitHub, and Discord require you to register callback URLs. For self-hosted Supabase, the callback URL follows this pattern:

https://api.yourdomain.com/auth/v1/callback

The callback URL must:

  1. Use HTTPS (most providers reject HTTP except for localhost)
  2. Match your API_EXTERNAL_URL exactly
  3. Include the /auth/v1/callback path

For each OAuth provider, add this to your docker-compose.yml:

services:
  auth:
    environment:
      GOTRUE_EXTERNAL_GOOGLE_ENABLED: true
      GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: your-client-id
      GOTRUE_EXTERNAL_GOOGLE_SECRET: your-client-secret
      GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: https://api.yourdomain.com/auth/v1/callback

For a complete guide on configuring all OAuth providers, see our OAuth providers setup guide.

Setting Up the Redirect Allowlist

The GOTRUE_URI_ALLOW_LIST variable determines which URLs your application can redirect to after authentication. This is a security feature—without it, attackers could craft malicious links that redirect users to phishing sites after login.

The allowlist supports glob patterns:

GOTRUE_URI_ALLOW_LIST: https://app.yourdomain.com/*,https://staging.yourdomain.com/*,http://localhost:3000/*

Common patterns:

PatternMatches
https://app.example.com/*Any path on your production domain
https://*.example.com/*Any subdomain (staging, dev, etc.)
http://localhost:*/*Any localhost port (development)

When your frontend calls signInWithOAuth or signUp, the redirectTo parameter must match an allowlist entry:

// This will work if https://app.yourdomain.com/* is in the allowlist
await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://app.yourdomain.com/dashboard'
  }
})

If the redirect URL isn't allowlisted, the user will be redirected to GOTRUE_SITE_URL instead—silently, with no error message.

Email Template URL Configuration

Email templates in self-hosted Supabase use Go template variables that get replaced when emails are sent. The most important one is {{ .ConfirmationURL }}, which constructs the full verification URL.

The confirmation URL is built from:

  1. API_EXTERNAL_URL (base)
  2. The mailer URL path (e.g., /auth/v1/verify)
  3. Query parameters (token, type, redirect)

If your emails contain incorrect URLs, check these variables:

services:
  auth:
    environment:
      # Base URL for email links
      API_EXTERNAL_URL: https://api.yourdomain.com
      
      # Path configurations (usually don't need to change these)
      GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify

For custom email templates with SMTP configuration, see our SMTP and email templates guide.

Server-Side Rendering Considerations

If you're using Next.js, SvelteKit, or another SSR framework, there's a catch: by default, Supabase returns the session in URL fragments (after #), which aren't accessible server-side.

To handle this properly, configure your email templates to use the PKCE flow:

services:
  auth:
    environment:
      GOTRUE_MAILER_AUTOCONFIRM: false
      GOTRUE_SECURITY_CAPTCHA_ENABLED: false

Then customize your email template to redirect to a server-side callback route:

<h2>Confirm your email</h2>
<p>Click the link below to confirm your email address:</p>
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">
  Confirm Email
</a>

Your server-side route can then exchange the token:

// /auth/confirm/route.ts (Next.js App Router)
import { createClient } from '@/utils/supabase/server'
import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const token_hash = searchParams.get('token_hash')
  const type = searchParams.get('type')
  
  if (token_hash && type) {
    const supabase = await createClient()
    const { error } = await supabase.auth.verifyOtp({ 
      token_hash, 
      type 
    })
    
    if (!error) {
      redirect('/dashboard')
    }
  }
  
  redirect('/auth/error')
}

Troubleshooting Common Issues

Symptom: Clicking email confirmation opens http://kong/... or http://localhost:8000/...

Solution: Set API_EXTERNAL_URL to your public domain:

API_EXTERNAL_URL: https://api.yourdomain.com

OAuth Callback Shows "Invalid Request"

Symptom: After OAuth provider login, you see an error instead of being redirected

Solution:

  1. Verify GOTRUE_EXTERNAL_{PROVIDER}_REDIRECT_URI matches your API_EXTERNAL_URL
  2. Ensure the same URL is registered in your OAuth provider's console
  3. Check that HTTPS is configured (most providers require it)

redirectTo Parameter Ignored

Symptom: Users always land on the site URL regardless of redirectTo

Solution: Add the redirect destination to GOTRUE_URI_ALLOW_LIST:

GOTRUE_URI_ALLOW_LIST: https://app.yourdomain.com/*,https://your-other-domain.com/*

"Token Has Expired" on Email Confirmation

Symptom: Clicking confirmation link shows token expired error

Causes:

  1. Email tracking: Your email provider's link tracking consumes the token before the user clicks. Disable email tracking in your SMTP provider.
  2. Safe Links: Microsoft/corporate email systems prefetch URLs. Consider using the PKCE flow instead.
  3. Token expiry: Default is 24 hours. Increase with GOTRUE_MAILER_OTP_EXP:
GOTRUE_MAILER_OTP_EXP: 86400  # 24 hours in seconds

Symptom: Magic link emails send but clicking them does nothing

Solution: Ensure GOTRUE_SITE_URL is set to your frontend URL (not your API URL):

# Wrong
GOTRUE_SITE_URL: https://api.yourdomain.com

# Correct  
GOTRUE_SITE_URL: https://app.yourdomain.com

Complete Configuration Example

Here's a complete, production-ready URL configuration:

services:
  auth:
    environment:
      # Core URL Configuration
      API_EXTERNAL_URL: https://api.yourdomain.com
      GOTRUE_SITE_URL: https://app.yourdomain.com
      GOTRUE_URI_ALLOW_LIST: https://app.yourdomain.com/*,https://staging.yourdomain.com/*,http://localhost:3000/*
      
      # Email URL Paths
      GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify
      GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify
      
      # OAuth Example (Google)
      GOTRUE_EXTERNAL_GOOGLE_ENABLED: true
      GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
      GOTRUE_EXTERNAL_GOOGLE_SECRET: ${GOOGLE_CLIENT_SECRET}
      GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI: https://api.yourdomain.com/auth/v1/callback

Simplifying URL Configuration with Supascale

Manually editing docker-compose.yml for URL configuration works, but it's tedious and error-prone—especially when managing multiple environments or projects.

Supascale provides a configuration UI for self-hosted Supabase that handles these settings through a dashboard:

  • Custom domains: Bind your domains with automatic SSL through the domain binding feature
  • OAuth providers: Configure Google, GitHub, Discord, and more through a visual interface instead of environment variables
  • Environment management: Preview and production configurations without manual file editing

For teams running multiple Supabase projects, having a central management interface prevents configuration drift and reduces deployment errors.

Wrapping Up

Redirect URL configuration in self-hosted Supabase comes down to three things:

  1. Set API_EXTERNAL_URL to your public API domain
  2. Set GOTRUE_SITE_URL to your frontend application
  3. Populate GOTRUE_URI_ALLOW_LIST with all valid redirect destinations

Get these right, and your email confirmations, OAuth flows, and magic links will work as expected. Get them wrong, and you'll chase phantom bugs through your auth flow.

For more on securing your self-hosted authentication setup, see our guides on JWT and session security and production hardening.


Further Reading