Vai al contenuto

Basi della sicurezza

Perché i link email di Supabase reindirizzano a localhost:3000

Se Supabase reindirizza a localhost:3000, la Site URL non è mai stata cambiata o il tuo indirizzo manca in Redirect URLs. La soluzione, e il jolly da evitare.

Vlad Tkachenko10 min di lettura
Un cartello indicatore la cui freccia illuminata punta lontano dalla casa a cui dovrebbe portare, verso un portatile con lo schermo spento.

In breve

  • Un reindirizzamento di Supabase a localhost:3000 viene dalla Site URL della pagina URL Configuration. Un progetto parte con http://localhost:3000, e ogni link email ci ricade quando l'indirizzo che la tua app ha chiesto non è nella lista Redirect URLs.
  • Chi ha premuto un link di registrazione di solito è già confermato. Supabase conferma l'account prima di mandarlo altrove, quindi può accedere sul tuo sito vero.
  • Imposta la Site URL sul tuo indirizzo online e metti in lista gli indirizzi esatti che usi. Un jolly così ampio da coprire l'app di qualcun altro permette a un link costruito ad arte di mandare lì i tuoi utenti, già con l'accesso fatto.

Un nuovo utente si registra sulla tua app online, apre l'email di conferma e preme il pulsante. Il suo browser apre una pagina che dice "This site can't be reached" e "localhost refused to connect" (così in un Chrome in inglese), sopra un indirizzo che comincia così:

http://localhost:3000/#access_token=…&expires_at=…&expires_in=…&refresh_token=…&sb=&token_type=bearer&type=signup

Se la tua app usa il flusso PKCE, finisce invece con ?code=…. Hai costruito l'app con Lovable, Bolt, v0, Cursor, Claude Code o Replit, in anteprima funzionava benissimo, e l'accesso con password funziona ancora.

Un reindirizzamento di Supabase a localhost significa che Supabase Auth, la parte di Supabase che fa accedere le persone, ha finito il suo lavoro e poi ha mandato la persona alla Site URL del tuo progetto, che dice ancora http://localhost:3000. Quell'indirizzo vuol dire "questo computer". Sul computer del tuo utente lì non c'è niente. La soluzione sono due impostazioni in una pagina della dashboard di Supabase.

Ecco la parte che le soluzioni rapide in rete sbagliano: il jolly che fa sparire questo messaggio con una riga è anche quello che permette al link di uno sconosciuto di mandare i tuoi utenti sulla sua pagina, già con l'accesso fatto. Le impostazioni qui sotto fanno funzionare i link e tengono chiusa quella porta.

Che cosa significa un reindirizzamento di Supabase a localhost?

Significa che Supabase ha mandato la persona all'indirizzo predefinito del tuo progetto, perché non ne ha accettato uno migliore.

Ogni email che Supabase invia per te, che sia una conferma di registrazione, un magic link, un ripristino della password o un invito, porta prima all'indirizzo del tuo progetto. La guida ai modelli di email di Supabase ne mostra la forma: https://project-ref.supabase.co/auth/v1/verify?token=…&type=email&redirect_to=https://example.com/path. Lì Supabase controlla il token e poi manda il browser a ciò che indica redirect_to.

Pensalo come un cartello indicatore alla porta di Supabase. Ogni persona che apre un link fa l'accesso alla porta e poi viene mandata avanti, con la chiave in mano, lungo una delle frecce. Le frecce le metti tu. La Site URL è la freccia che prendono tutti quando nessun'altra va bene, e in un progetto nuovo punta a http://localhost:3000: l'officina dove l'app è stata costruita, che sulla macchina di chiunque altro è una stanza vuota.

La guida ai reindirizzamenti di Supabase dice che la Site URL definisce il reindirizzamento predefinito quando nel codice non c'è nessun redirectTo, ti chiede di cambiarla da http://localhost:3000 al tuo indirizzo di produzione e definisce questa impostazione fondamentale per le conferme via email e i ripristini della password.

Perché Supabase ignora l'indirizzo che la mia app ha chiesto?

Perché quell'indirizzo non è nella tua lista Redirect URLs, e Supabase ripiega su un altro senza dirtelo.

La tua app può indicare dove le persone devono arrivare. Nella libreria JavaScript di Supabase è emailRedirectTo su signUp e signInWithOtp, e redirectTo su resetPasswordForEmail e signInWithOAuth. Poi Supabase Auth decide in una breve funzione, in quest'ordine:

  1. L'indirizzo che la tua app ha chiesto, se corrisponde a una voce della tua lista Redirect URLs o sta sullo stesso dominio della tua Site URL.
  2. L'indirizzo della pagina da cui è partita la richiesta, come l'ha riportato il browser, con la stessa regola.
  3. Altrimenti la Site URL. Supabase non segnala nessun errore, e la console della tua app non mostra niente.

Prende questa decisione quando l'email viene inviata, e l'indirizzo scelto finisce nel link. Quando qualcuno apre il link, Supabase ricontrolla quell'indirizzo con le tue impostazioni così come sono in quel momento. La stessa risposta di risoluzione dei problemi di Supabase riassume la regola in una frase: perché l'opzione redirectTo funzioni, devi inserire l'URL esatto nell'impostazione Redirect URLs.

Supabase prova l'indirizzo che la tua app ha chiesto, poi la pagina da cui è partita la richiesta. Quando nessuno dei due è nella tua lista, decide la Site URL.

La stessa impostazione decide dove "Sign in with Google" riporta le persone, quindi un pulsante Google che atterra su localhost ha la stessa causa. Un pulsante Google che si ferma prima, su una pagina con scritto "Unsupported provider: provider is not enabled", è invece un interruttore in Supabase e una lista su Google.

Perché non l'hai visto prima del lancio

Perché i due modi in cui hai fatto le prove non arrivano mai alla Site URL.

  • L'accesso con password avviene dentro la pagina. Niente link email e nessun passaggio dalla porta di Supabase, quindi il cartello non viene mai letto.
  • Se costruisci con Cursor o Claude Code e il tuo server di sviluppo era acceso sulla porta 3000 quando hai provato l'email, il link ha aperto la tua copia locale e sembrava funzionare.
  • Sul backend integrato di Lovable forse non ti capiterà mai. La documentazione sull'autenticazione di Lovable dice che imposta la Site URL sul tuo indirizzo lovable.app pubblicato quando pubblichi, e che aggiunge un dominio personalizzato alle redirect URL quando lo colleghi. La stessa pagina dice che le app che usano il tuo progetto Supabase non ricevono aggiornamenti automatici.

L'account è confermato anche se la pagina non si è caricata?

Sì, per una conferma di registrazione. Supabase conferma prima e manda avanti il browser solo dopo.

Nel codice che gestisce il link, l'account viene confermato prima che parta il reindirizzamento. La pagina che non si carica arriva a lavoro già fatto. Quindi di' alla persona che può accedere sul tuo sito online con la password che ha scelto. Premere di nuovo la stessa email non serve: il link funziona una volta, e la seconda pressione risponde "Email link is invalid or has expired".

Un magic link o un ripristino della password è diverso. Quell'accesso è finito su localhost ed è rimasto lì, quindi quando avrai corretto le impostazioni la persona avrà bisogno di una nuova email.

Come correggere il reindirizzamento di Supabase a localhost

Cambia la Site URL, poi metti in lista gli altri indirizzi a cui mandi le persone. Entrambi stanno nella pagina URL Configuration, sotto Authentication, nella dashboard di Supabase.

Imposta la Site URL sul tuo indirizzo online, con https:// davanti.https://yourapp.com, scritto per intero. Senza https://, Supabase lo legge come un percorso sul proprio indirizzo, e la persona finisce invece sul tuo progetto Supabase con "No API key found in request". Da lì in poi qualsiasi indirizzo di quel dominio viene accettato a qualsiasi percorso senza voce in lista, quindi https://yourapp.com/welcome funziona appena la Site URL è giusta.

Aggiungi gli altri indirizzi a Redirect URLs. La versione con www se le persone la usano, l'indirizzo pubblicato sul builder (https://yourapp.lovable.app, per esempio) se le persone si registrano lì, e nient'altro. Una voce è uno schema, quindi https://yourapp.com/** copre ogni pagina di quell'unico dominio.

Controlla che cosa chiede il tuo codice. Se passa window.location.origin, l'indirizzo è quello su cui è stata aperta la pagina, e ogni indirizzo che ne può uscire deve stare sul dominio della tua Site URL o nella lista. Confrontali lettera per lettera: con www o senza, con https o senza. Una voce senza /** in fondo corrisponde a quell'indirizzo esatto e a niente al di sotto.

Controlla i tuoi modelli di email. Un modello che costruisce il proprio link con {{ .SiteURL }} manda tutti alla Site URL, qualunque cosa abbia chiesto la tua app. La guida ai reindirizzamenti di Supabase dice che, usando l'opzione redirectTo, potresti dover sostituire {{ .SiteURL }} con {{ .RedirectTo }} nei tuoi modelli di email.

Cosa vediCosa cambiare
Ogni link apre localhost:3000La Site URL
I link aprono la tua Site URL invece dell'indirizzo che il tuo codice ha chiestoAggiungi quell'indirizzo a Redirect URLs, lettera per lettera
Il dominio online funziona e l'indirizzo pubblicato sul builder noAggiungi l'indirizzo pubblicato a Redirect URLs
Un link apre il tuo indirizzo Supabase con "No API key found in request"Aggiungi https:// alla Site URL
L'indirizzo che la tua app chiede è in lista e il link lo ignora lo stessoIl {{ .SiteURL }} nel modello di email

Poi registrati di nuovo con un indirizzo che non hai ancora usato. Un'email inviata prima della correzione con il link standard di Supabase, e aperta entro la sua ora, ora arriva sul tuo sito online, perché Supabase ricontrolla l'indirizzo quando il link viene aperto. Vale a meno che http://localhost:3000 sia ancora nella tua lista.

Quali Redirect URLs si possono consentire senza rischi?

Gli indirizzi esatti che sono tuoi. Un jolly va bene per i deploy di anteprima, e solo limitato al tuo team o progetto.

La guida ai reindirizzamenti di Supabase lo dice chiaramente: il "globstar" (**) è utile per lo sviluppo locale e gli indirizzi di anteprima, ma in produzione consiglia di fissare il percorso di reindirizzamento esatto della tua Site URL.

Il motivo è ciò che viaggia con la persona. Nel flusso predefinito, Supabase scrive la nuova sessione nell'indirizzo a cui la manda: il codice che lo costruisce aggiunge #access_token=…&refresh_token=… all'indirizzo di reindirizzamento. Qualsiasi pagina a quell'indirizzo può leggere quei due valori, e con quelli è quell'utente. Sul cartello, è la freccia a decidere chi riceve la chiave.

Quindi una voce che copre un indirizzo controllato da qualcun altro è una via d'accesso agli account dei tuoi utenti. Chiunque può chiedere al tuo progetto di mandare via email a uno dei tuoi utenti un link di accesso, usando la chiave pubblica che sta nella tua app per scelta, e indicare la propria pagina come destinazione. L'email che arriva viene davvero dalla tua app. Il flusso PKCE non cambia le cose, perché chi ha scritto quel link ha avviato l'accesso, e PKCE consegna la sessione a chi l'ha avviato.

Gli schemi che coprono indirizzi che altri possono creare:

  • ** da solo copre ogni indirizzo esistente.
  • https://*.vercel.app/** copre ogni deploy di Vercel su internet, e chiunque può crearne uno. L'esempio di Supabase stesso contiene il tuo team: https://*-<team-or-account-slug>.vercel.app/**.
  • https://*.lovable.app/**, https://*.netlify.app/** e la stessa forma sul dominio di qualsiasi builder fanno lo stesso per quel builder. Anche l'esempio di Supabase per Netlify è limitato: https://**--my_org.netlify.app/**.
Una voce esatta rimanda a casa un link costruito ad arte. Un jolly che copre le app degli altri gli lascia consegnare loro l'accesso.

Se hai appena risolto incollando un jolly, ricontrolla la lista oggi stesso.

Il lancio è anche il momento in cui si guardano le altre impostazioni che contano solo in produzione. La nostra scansione gratuita legge la tua app online dall'esterno e segnala ciò che uno sconosciuto può raggiungere: tabelle che rispondono a chiunque, chiavi segrete nel codice che la tua app invia, header mancanti, il certificato. Ci vogliono circa 20 secondi e non serve un account: scansiona la tua app.

Tenere d'occhio l'app online dopo il lancio

Questa impostazione si corregge una volta. Quello che la tua app invia ai visitatori cambia a ogni deploy, e quella parte si può sorvegliare dall'esterno.

Reeve Monitor continua a controllare la tua app online:

  • se risponde, ogni 60 secondi, così "il link non funziona" si distingue da "il sito è giù"
  • tutti e nove i controlli ogni ora, su un massimo di tre app, comprese le chiavi segrete nel codice che la tua app invia ai visitatori
  • un messaggio quando un risultato cambia, così un problema uscito con un deploy successivo ti raggiunge senza che tu debba andare a guardare
  • un report mensile, in linguaggio semplice, di quello che ha visto

Reeve Care tiene una copia del tuo database Supabase, account compresi:

  • una copia cifrata secondo una pianificazione, sullo spazio di archiviazione di Reeve nell'UE, fuori dal tuo account Supabase
  • lo schema auth in cui vivono gli account dei tuoi utenti, così tornano insieme a tutto il resto
  • ogni copia verificata prima che conti, contando le righe di ogni tabella
  • anche i tuoi file caricati, appena colleghi una credenziale di Storage
  • un ripristino in un clic, con una copia di sicurezza dello stato attuale presa prima
  • tutto quello che fa Monitor

Cosa fare adesso

Cosa fare

  • Imposta la Site URL nella pagina URL Configuration sul tuo indirizzo online, https:// compreso.
  • Aggiungi a Redirect URLs gli altri indirizzi esatti a cui mandi le persone, come la versione con www e l'indirizzo pubblicato sul builder.
  • Assicurati che ogni indirizzo che il tuo codice passa come emailRedirectTo o redirectTo sia in lista, e usa {{ .RedirectTo }} nei modelli che devono seguirlo.
  • Di' a chi ha visto la pagina di localhost dopo la registrazione che il suo account è confermato. I magic link e i ripristini della password richiedono una nuova email.
  • Limita i jolly alle anteprime del tuo team, e non consentire mai tutte le app del dominio di un builder.

Se le persone premono il nuovo link e si sentono dire che è scaduto, quel messaggio ha una pagina tutta sua. La checklist di sicurezza in 10 minuti copre ciò che di solito resta aperto in un'app appena lanciata.

FAQ

Perché la mia email di conferma di Supabase apre localhost:3000?

Perché Supabase ha mandato la persona alla Site URL del tuo progetto, e la Site URL è ancora http://localhost:3000. Supabase la usa ogni volta che l'indirizzo chiesto dalla tua app manca dalla lista Redirect URLs, o quando la tua app non ne ha chiesto nessuno. Cambiala nella pagina URL Configuration, sotto Authentication.

L'account è confermato se il link ha aperto localhost?

Sì, per una conferma di registrazione. Supabase conferma prima l'indirizzo e solo dopo manda avanti il browser, quindi la pagina che fallisce arriva a lavoro già fatto. La persona può accedere sul tuo sito online con la password che ha scelto. Un magic link o un ripristino della password va inviato di nuovo.

Ho aggiunto il mio sito a Redirect URLs e va ancora su localhost. Perché?

La voce deve corrispondere lettera per lettera all'indirizzo offerto a Supabase: con www o senza, con https o senza, e il percorso, perché una voce senza /** in fondo corrisponde solo a quell'indirizzo preciso. Se corrispondono e il link la ignora ancora, il tuo modello di email costruisce il link con {{ .SiteURL }}. E correggi la Site URL stessa, perché è lì che ricade tutto il resto.

Devo aggiungere la mia Site URL alla lista Redirect URLs?

No. Supabase accetta qualsiasi indirizzo sul dominio stesso della Site URL senza voce in lista, a qualsiasi percorso. La lista serve per gli altri indirizzi: una versione con www, l'indirizzo pubblicato sul builder, i deploy di anteprima.

Posso usare un jolly nelle Redirect URLs di Supabase?

Per le anteprime, limitato al tuo team o progetto, come negli esempi di Supabase. Mai uno che copra indirizzi che altri possono creare, come tutte le app su vercel.app o lovable.app. Supabase consiglia l'indirizzo esatto del tuo sito in produzione.

Scritto da

Vlad Tkachenko

Fondatore di Reeve

Passo le giornate a guardare app costruite con Lovable, Bolt, v0, Cursor e Replit, e la breve lista di errori che vi ricompaiono di continuo.

Altro sull'autore

Da leggere dopo

Tutti gli articoli

Fai il backup del database Supabase prima della prossima modifica

Reeve ne tiene una copia cifrata a intervalli programmati e la ripristina per te se qualcosa va storto.

Vedi i backup Supabase

Controllo esterno automatizzato, non un audit completo. L'assenza di risultati non è una garanzia di sicurezza.