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:
- Site URL: The default redirect destination after authentication
- Redirect URLs: Allowed destinations for
redirectToparameters - 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 noredirectTois specified.GOTRUE_URI_ALLOW_LIST: Security allowlist for redirect destinations. AnyredirectToparameter 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:
- Use HTTPS (most providers reject HTTP except for localhost)
- Match your
API_EXTERNAL_URLexactly - Include the
/auth/v1/callbackpath
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:
| Pattern | Matches |
|---|---|
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:
API_EXTERNAL_URL(base)- The mailer URL path (e.g.,
/auth/v1/verify) - 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
Email Links Redirect to Wrong URL
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:
- Verify
GOTRUE_EXTERNAL_{PROVIDER}_REDIRECT_URImatches yourAPI_EXTERNAL_URL - Ensure the same URL is registered in your OAuth provider's console
- 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:
- Email tracking: Your email provider's link tracking consumes the token before the user clicks. Disable email tracking in your SMTP provider.
- Safe Links: Microsoft/corporate email systems prefetch URLs. Consider using the PKCE flow instead.
- Token expiry: Default is 24 hours. Increase with
GOTRUE_MAILER_OTP_EXP:
GOTRUE_MAILER_OTP_EXP: 86400 # 24 hours in seconds
Magic Links Not Working
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:
- Set
API_EXTERNAL_URLto your public API domain - Set
GOTRUE_SITE_URLto your frontend application - Populate
GOTRUE_URI_ALLOW_LISTwith 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.
