BusiRocket

Artículo

El login de Supabase redirige a localhost tras publicar: los cuatro ajustes que hay que corregir

Publicado el

La app ya está publicada en tu dominio. Un usuario se registra, pulsa el enlace de confirmación del correo y aterriza en http://localhost:3000 con un "No se puede acceder a este sitio". O termina el login con Google y le devuelve a localhost:8080, o a la preview de Lovable, o a la dirección lovable.app en lugar de a tu dominio.

Al navegador del usuario no le pasa nada. Supabase Auth decide a dónde mandar a alguien después del login a partir de cuatro ajustes, y al menos uno sigue apuntando al sitio donde se construyó la app. El valor por defecto de la Site URL en Supabase es http://localhost:3000, y por eso esa dirección aparece incluso en apps que nunca han corrido en el puerto 3000.

Los cuatro sitios, en el orden en que conviene revisarlos:

  1. La Site URL en los ajustes de autenticación.
  2. La lista de Redirect URLs permitidas, justo al lado.
  3. La dirección de redirección que pasa el código cuando inicia un login.
  4. Las plantillas de correo, más los correos que ya se han enviado.

El login con Google añade un quinto, la consola de Google Cloud, que está al final.

Cómo elige Supabase la dirección

Cuando tu código inicia un login, puede pasar una dirección de redirección: redirectTo para signInWithOAuth y resetPasswordForEmail, emailRedirectTo para signUp y signInWithOtp. Supabase manda al usuario ahí solo si la dirección coincide con una entrada de la lista de Redirect URLs. Si el código no pasa nada, o pasa algo que no está en la lista, el usuario va a la Site URL.

Así que "siempre va a localhost" casi siempre significa una de dos cosas: la Site URL sigue siendo localhost y el código no pasa nada, o el código pasa una dirección de producción que no está en la lista y Supabase cae en una Site URL que sigue siendo localhost.

1. Pon tu dominio real en la Site URL

La Site URL es el destino por defecto y la base de los enlaces de los correos de autenticación. Tiene que ser tu dirección de producción con el protocolo y el host exactos que ven los usuarios: aquí https://www.example.com y https://example.com son direcciones distintas.

Dónde está depende de cómo esté montado el backend del proyecto:

  • Tu propio proyecto de Supabase (Lovable conectado a Supabase, Bolt, v0 o cualquier app hecha a mano): panel de Supabase, Authentication, URL Configuration.
  • Lovable Cloud: More, luego Cloud, luego Users, luego Auth settings, en Advanced. La documentación de Lovable dice que pone como Site URL tu dirección lovable.app al publicar, y que añade el dominio personalizado a las Redirect URLs cuando lo conectas. No toca una Site URL que hayas puesto tú. Así que si los usuarios acaban en la dirección lovable.app en vez de en tu dominio, pon aquí el dominio personalizado como Site URL.

Las apps con su propio proyecto de Supabase no tienen nada de esa automatización; la documentación de Lovable lo dice explícitamente. Cada dirección hay que añadirla a mano.

2. Mete todas las direcciones reales en la lista de Redirect URLs

La lista tiene que contener cada dirección a la que tu app devuelve a los usuarios. Supabase admite comodines: * coincide con cualquier cosa menos . y /, mientras que ** coincide con cualquier cosa, barras incluidas. Una lista razonable para una app típica:

https://www.example.com/**
https://example.com/**
http://localhost:8080/**

La última línea mantiene funcionando el desarrollo en local; usa el puerto de tu servidor de desarrollo (los proyectos Vite de Lovable usan el 8080, una app Vite normal el 5173, Next.js el 3000). Supabase recomienda usar ** para las direcciones locales y de preview y ser más exacto en producción, por ejemplo https://www.example.com/auth/callback si es tu única ruta de vuelta. Un comodín amplio en producción no es un agujero por sí solo, pero con una ruta exacta es más fácil saber qué está permitido.

Para las preview deployments de Vercel, Supabase documenta un patrón como https://*-your-team.vercel.app/**, para que las previews puedan hacer login sin que tengas que añadir cada una.

3. Busca el localhost escrito a fuego en el código

Las herramientas de IA suelen escribir la dirección de redirección como un literal mientras construyen en la preview, y sobrevive a todos los cambios posteriores. Busca en el código localhost, redirectTo, emailRedirectTo y el hostname de la preview. Un culpable típico:

await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: { redirectTo: 'http://localhost:8080/dashboard' },
})

En una app que solo corre en el navegador (Vite, React), construye la dirección a partir de donde está realmente el usuario:

await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: { redirectTo: `${window.location.origin}/dashboard` },
})

y lo mismo con emailRedirectTo en signUp y redirectTo en resetPasswordForEmail. En apps renderizadas en servidor (Next.js), window no existe en el servidor, así que lee la dirección de una variable de entorno como NEXT_PUBLIC_SITE_URL y ponla en cada entorno. La documentación de Supabase sugiere justo eso para Vercel.

Lo que produzca el código tiene que coincidir con una entrada de la sección 2, o Supabase cae en silencio a la Site URL y vuelves a estar donde empezaste.

4. Arregla las plantillas de correo y manda correos nuevos

Los correos de confirmación, magic link, invitación y cambio de contraseña se generan a partir de plantillas en Authentication, luego Emails (Email Templates en los paneles antiguos). Las plantillas por defecto usan {{ .ConfirmationURL }}, que ya incluye la dirección de redirección. Las plantillas personalizadas a veces construyen el enlace con {{ .SiteURL }}, que ignora emailRedirectTo por completo. La documentación de Supabase dice que, cuando pasas una dirección de redirección, puede que tengas que cambiar {{ .SiteURL }} por {{ .RedirectTo }} en las plantillas.

Y luego el paso que más tiempo hace perder: los correos ya enviados conservan el enlace antiguo. Después de cambiar cualquiera de estos ajustes, prueba con un registro nuevo o una petición de cambio de contraseña nueva, no con el correo de hace una hora.

5. Login con Google: la parte de Google Cloud

Con tu propio cliente OAuth, Google necesita conocer dos direcciones, y son distintas:

  • Authorized redirect URIs: el callback de Supabase, no tu web. En un proyecto de Supabase alojado tiene esta pinta: https://<project-ref>.supabase.co/auth/v1/callback, y el valor exacto aparece en el panel de Supabase, en la página del proveedor Google. Google redirige a Supabase; Supabase redirige después a tu app usando las secciones 1 a 3.
  • Authorized JavaScript origins: las direcciones de tu web, incluido el dominio personalizado.

Un error redirect_uri_mismatch de Google significa que la primera lista está mal. Una pantalla de Google que va bien seguida de una página de localhost significa que Google está bien y el problema está en las secciones 1 a 3. En Lovable Cloud con el login de Google gestionado por Lovable, Lovable se encarga de la parte de Google; si cambiaste a tus propias credenciales, la documentación de Lovable avisa de que cada dominio nuevo también necesita su redirect URI añadida en Google Cloud.

La redirección llega a tu dominio y da un 404

Una última variante: el usuario llega al dominio correcto, por ejemplo https://example.com/auth/callback?code=..., y le sale un 404. Eso no es Supabase. La app es una single-page app, el hosting estático no tiene ningún archivo en /auth/callback y nadie ha configurado el hosting para servir index.html en todas las rutas. En Vercel se arregla con un vercel.json con un rewrite:

{ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] }

Netlify usa un archivo _redirects con /* /index.html 200. Si la página carga pero el usuario sigue sin sesión, comprueba que el cliente se crea con el detectSessionInUrl por defecto, que lee el código de la dirección y termina el login.

Pruébalo como un usuario

Abre una ventana privada en un dispositivo que nunca haya visto la preview, regístrate con una dirección nueva, pulsa el correo, cierra sesión, entra con Google y cambia la contraseña. Cada flujo tiene que terminar en tu dominio con el usuario dentro. Después hazlo una vez con www y otra sin él, porque una sesión creada en un host no se ve en el otro.

Si todo eso pasa y los usuarios con sesión se encuentran errores de permisos al guardar por primera vez, la siguiente parada es el error "new row violates row-level security policy", que a menudo resulta ser una sesión que no existe.

Las redirecciones del login son uno de los fallos típicos al pasar una app de Lovable de la preview a producción. Si ya has arreglado esto y se te han roto otras dos cosas, probablemente la app necesite que alguien la lea de principio a fin. Eso es el diagnóstico de nuestro rescate de apps hechas con IA: 450 €, 48 horas laborables y un informe por escrito de qué está roto y cuánto cuesta cada arreglo. Si sigues con el rescate (entre 1.500 € y 6.000 €, con precio cerrado por alcance), los 450 € se descuentan.

Siguiente paso

¿Te suena este problema en tu proyecto?

Cuéntanoslo y te decimos cómo lo abordaríamos, en qué orden y con qué coste.