Set the production URLs at the auth provider
Change the Site URL to your production origin and add the exact production callback path to the redirect allow list. Supabase recommends the exact path over a wildcard for production; wildcard patterns for Vercel and Netlify previews belong in a separate entry. Register the production callback with the OAuth provider as well, or use a separate OAuth app per environment, as Auth.js suggests.
await supabase.auth.signInWithOAuth({
provider: 'github',
options: { redirectTo: 'https://app.example.com/auth/callback' },
})
Fix the secret and the proxy headers
Generate the session secret once and store it as a host environment variable, so every instance and every deploy uses the same value. If the app sits behind your own reverse proxy or runs in Docker, tell the auth library to trust the forwarded host. Check that the proxy forwards the Host and X-Forwarded-Proto headers and keeps the query string on rewrites.
# generate once: openssl rand -base64 33
AUTH_SECRET=<generated value>
AUTH_TRUST_HOST=true
Test the whole loop on the production domain with a real inbox
Sign up, verify, sign out, reset, and sign in again on the live domain, using a Gmail address and one other provider you do not control. Do it from a clean browser profile and from a phone. Staging on a platform subdomain proves little about a custom domain or a proxy.
Send from a verified domain through a production provider
Add your own domain at the email provider and move off the built-in or testing sender. If your host blocks SMTP, use the provider's HTTPS API instead; Railway recommends this on every plan. Check the provider's bounce and suppression lists after launch, because one address that hard-bounces can quietly stop mail to that person.
Publish SPF, DKIM and DMARC for the sending domain
Add the DNS records the provider gives you, then add a DMARC record (a policy of none is accepted to start). Register the domain in Google Postmaster Tools and keep the spam rate well under 0.3%. Keep marketing mail on a separate sender or subdomain from sign-in mail.
Make links survive scanners and build them from configuration
Send a code the person types, or link to a page of yours with a confirm button, so a GET request alone does not spend the token. Supabase documents both patterns, and verifyOtp completes the code flow. Make tokens single use with a short expiry, and build the link from a configured base URL, not from the request's Host header, as OWASP advises.
const { data, error } = await supabase.auth.verifyOtp({
email,
token,
type: 'email',
})
Keep a recovery path for your own account
Make sure at least one other working route exists into the hosting, DNS and email-provider accounts: stored backup codes for two-factor authentication, a second verified owner, and a recovery address you control. Check this before launch, because account recovery after losing a 2FA key can be impossible.