Sicherheitsgrundlagen
Warum Supabase-E-Mail-Links auf localhost:3000 weiterleiten
Leitet Supabase auf localhost:3000 um, ist die Site URL unverändert, oder deine Adresse fehlt in Redirect URLs. Die Lösung und der Platzhalter, den du meidest.

Kurz gesagt
- Eine Weiterleitung von Supabase auf localhost:3000 kommt von der Site URL auf der Seite URL Configuration. Ein Projekt beginnt mit http://localhost:3000, und jeder E-Mail-Link fällt darauf zurück, wenn die Adresse, die deine App verlangt, nicht auf der Liste Redirect URLs steht.
- Wer einen Registrierungslink gedrückt hat, ist meist schon bestätigt. Supabase bestätigt das Konto, bevor es die Person weiterschickt, also kann sie sich auf deiner echten Seite anmelden.
- Setz die Site URL auf deine Live-Adresse und trag die genauen Adressen ein, die du benutzt. Ein Platzhalter, der so weit reicht, dass er die App eines anderen trifft, lässt einen präparierten Link deine Nutzer angemeldet dorthin schicken.
Ein neuer Nutzer registriert sich in deiner Live-App, öffnet die Bestätigungs-E-Mail und drückt auf den Knopf. Sein Browser öffnet eine Seite mit "This site can't be reached" und "localhost refused to connect" (so in einem englischen Chrome), über einer Adresse, die so beginnt:
http://localhost:3000/#access_token=…&expires_at=…&expires_in=…&refresh_token=…&sb=&token_type=bearer&type=signup
Nutzt deine App den PKCE-Ablauf, endet sie stattdessen auf ?code=…. Du hast
die App mit Lovable, Bolt, v0, Cursor, Claude Code oder Replit gebaut, in der
Vorschau lief sie tadellos, und die Anmeldung mit Passwort funktioniert immer
noch.
Eine Weiterleitung von Supabase auf localhost heißt: Supabase Auth, der Teil
von Supabase, der Leute anmeldet, hat seine Arbeit erledigt und die Person dann
zur Site URL deines Projekts geschickt, und die steht noch auf
http://localhost:3000. Diese Adresse bedeutet "dieser Computer". Auf dem
Computer deines Nutzers ist dort nichts. Die Lösung sind zwei Einstellungen auf
einer Seite deines Supabase-Dashboards.
Hier liegt der Teil, den die schnellen Lösungen im Netz falsch machen: Der Platzhalter, der diese Meldung mit einer Zeile verschwinden lässt, ist auch der, mit dem der Link eines Fremden deine Nutzer schon angemeldet auf seine Seite schickt. Die Einstellungen unten bringen die Links zum Laufen und halten diese Tür zu.
Was bedeutet eine Weiterleitung von Supabase auf localhost?
Sie bedeutet, dass Supabase die Person zur Standardadresse deines Projekts geschickt hat, weil es keine bessere angenommen hat.
Jede E-Mail, die Supabase für dich verschickt, ob Bestätigung der
Registrierung, Magic Link, Zurücksetzen des Passworts oder Einladung, verlinkt
zuerst auf die eigene Adresse deines Projekts. Supabases
Anleitung zu den E-Mail-Vorlagen
zeigt die Form:
https://project-ref.supabase.co/auth/v1/verify?token=…&type=email&redirect_to=https://example.com/path.
Dort prüft Supabase das Token und schickt den Browser dann weiter zu dem, was
redirect_to nennt.
Stell es dir als Wegweiser an Supabases Tür vor. Jede Person, die einen Link
öffnet, wird an der Tür angemeldet und dann, den Schlüssel in der Hand, einen
der Arme entlang weitergeschickt. Die Arme stellst du auf. Die Site URL ist der
Arm, den alle nehmen, wenn kein anderer passt, und bei einem neuen Projekt zeigt
er auf http://localhost:3000: die Werkstatt, in der die App gebaut wurde, auf
jedem anderen Rechner ein leerer Raum.
Supabases
Anleitung zu Weiterleitungen
sagt, dass die Site URL die Standard-Weiterleitung festlegt, wenn im Code kein
redirectTo angegeben ist. Sie fordert dich auf, sie von
http://localhost:3000 auf deine Produktionsadresse zu ändern, und nennt die
Einstellung entscheidend für E-Mail-Bestätigungen und das Zurücksetzen von
Passwörtern.
Warum übergeht Supabase die Adresse, die meine App verlangt hat?
Weil diese Adresse nicht auf deiner Liste Redirect URLs steht, und Supabase fällt zurück, ohne es dir zu sagen.
Deine App kann angeben, wo Leute landen sollen. In Supabases
JavaScript-Bibliothek ist das emailRedirectTo bei signUp und
signInWithOtp und redirectTo bei resetPasswordForEmail und
signInWithOAuth. Supabase Auth entscheidet dann in
einer kurzen Funktion,
in dieser Reihenfolge:
- Die Adresse, die deine App verlangt hat, wenn sie zu einem Eintrag auf deiner Liste Redirect URLs passt oder auf derselben Domain wie deine Site URL liegt.
- Die Adresse der Seite, von der die Anfrage kam, so wie der Browser sie gemeldet hat, nach derselben Regel.
- Sonst die Site URL. Supabase meldet keinen Fehler, und die Konsole deiner App zeigt nichts.
Diese Entscheidung fällt, wenn die E-Mail verschickt wird, und die gewählte
Adresse kommt in den Link. Öffnet jemand den Link, prüft Supabase diese Adresse
noch einmal gegen deine Einstellungen, so wie sie in diesem Moment sind.
Supabases eigene
Antwort in der Fehlersuche
fasst die Regel in einem Satz: Damit die übergebene Option redirectTo
funktioniert, muss genau diese URL in der Einstellung Redirect URLs stehen.
Dieselbe Einstellung entscheidet, wohin "Sign in with Google" die Leute zurückschickt, ein Google-Knopf, der auf localhost landet, hat also dieselbe Ursache. Bleibt ein Google-Knopf schon früher stehen, auf einer Seite mit "Unsupported provider: provider is not enabled", geht es dagegen um einen Schalter in Supabase und eine Liste bei Google.
Warum du es vor dem Start nicht gesehen hast
Weil die beiden Wege, auf denen du getestet hast, die Site URL nie erreichen.
- Die Anmeldung mit Passwort meldet Leute innerhalb der Seite an. Kein E-Mail-Link und kein Weg durch Supabases Tür, also wird der Wegweiser nie gelesen.
- Wenn du mit Cursor oder Claude Code baust und dein Entwicklungsserver auf Port 3000 lief, als du die E-Mail getestet hast, hat der Link deine eigene lokale Kopie geöffnet und sah aus, als hätte er funktioniert.
- Im eingebauten Backend von Lovable triffst du vielleicht nie darauf.
Lovables Dokumentation zur Anmeldung
sagt, dass es die Site URL beim Veröffentlichen auf deine Adresse unter
lovable.appsetzt und eine eigene Domain zu den Redirect URLs hinzufügt, wenn du sie verbindest. Dieselbe Seite sagt, dass Apps mit deinem eigenen Supabase-Projekt keine automatischen Aktualisierungen bekommen.
Ist das Konto bestätigt, obwohl die Seite nicht geladen hat?
Ja, bei einer Bestätigung der Registrierung. Supabase bestätigt zuerst und schickt den Browser erst danach weiter.
In dem Code, der den Link verarbeitet, wird das Konto bestätigt, bevor die Weiterleitung hinausgeht. Die Seite, die nicht lädt, kommt, wenn die Arbeit schon erledigt ist. Sag der Person also, dass sie sich auf deiner Live-Seite mit dem Passwort anmelden kann, das sie gewählt hat. Dieselbe E-Mail noch einmal zu drücken hilft nicht: Der Link funktioniert einmal, und der zweite Druck antwortet mit "Email link is invalid or has expired".
Ein Magic Link oder ein Zurücksetzen des Passworts ist etwas anderes. Diese Anmeldung ging nach localhost und blieb dort, also braucht die Person eine neue E-Mail, sobald du die Einstellungen korrigiert hast.
So behebst du die Weiterleitung von Supabase auf localhost
Ändere die Site URL und trag dann die anderen Adressen ein, zu denen du Leute schickst. Beides steht auf der Seite URL Configuration unter Authentication in deinem Supabase-Dashboard.
Setz die Site URL auf deine Live-Adresse, mit https:// davor.https://yourapp.com, vollständig geschrieben. Fehlt das https://, liest
Supabase sie als Pfad auf seiner eigenen Adresse, und die Person landet
stattdessen bei deinem Supabase-Projekt mit
"No API key found in request". Jede
Adresse auf dieser Domain wird danach unter jedem Pfad ohne Listeneintrag
angenommen, https://yourapp.com/welcome funktioniert also, sobald die Site
URL stimmt.
Trag die anderen Adressen in Redirect URLs ein. Die Version mit www, wenn
Leute sie benutzen, die veröffentlichte Adresse beim Builder
(https://yourapp.lovable.app zum Beispiel), wenn Leute sich dort
registrieren, und sonst nichts. Ein Eintrag ist ein Muster, also deckt
https://yourapp.com/** jede Seite auf dieser einen Domain ab.
Prüf, was dein Code verlangt. Übergibt er window.location.origin, ist die
Adresse die, unter der die Seite geöffnet wurde, und jede Adresse, die dabei
herauskommen kann, muss auf der Domain deiner Site URL oder auf der Liste
liegen. Vergleich sie Zeichen für Zeichen: mit www oder ohne, mit https
oder ohne. Ein Eintrag ohne /** am Ende passt auf genau diese Adresse und auf
nichts darunter.
Prüf deine E-Mail-Vorlagen. Eine Vorlage, die ihren Link selbst aus
{{ .SiteURL }} baut, schickt alle zur Site URL, egal was deine App verlangt
hat. Supabases Anleitung zu Weiterleitungen sagt, dass du bei Verwendung der
Option redirectTo in deinen E-Mail-Vorlagen womöglich {{ .SiteURL }} durch
{{ .RedirectTo }} ersetzen musst.
| Was du siehst | Was du änderst |
|---|---|
Jeder Link öffnet localhost:3000 | Die Site URL |
| Links öffnen deine Site URL statt der Adresse, die dein Code verlangt hat | Trag diese Adresse in Redirect URLs ein, Zeichen für Zeichen |
| Die Live-Domain funktioniert, die veröffentlichte Adresse beim Builder nicht | Trag die veröffentlichte Adresse in Redirect URLs ein |
| Ein Link öffnet deine Supabase-Adresse mit "No API key found in request" | Setz https:// vor die Site URL |
| Die Adresse, die deine App verlangt, steht auf der Liste, und der Link übergeht sie trotzdem | Das {{ .SiteURL }} in der E-Mail-Vorlage |
Registrier dich dann noch einmal mit einer Adresse, die du noch nicht benutzt
hast. Eine E-Mail, die vor der Korrektur mit Supabases Standardlink verschickt
und innerhalb ihrer Stunde geöffnet wird, landet jetzt auf deiner Live-Seite,
weil Supabase die Adresse beim Öffnen des Links noch einmal prüft. Das gilt,
solange http://localhost:3000 nicht mehr auf deiner Liste steht.
Welche Redirect URLs kann man gefahrlos zulassen?
Die genauen Adressen, die dir gehören. Ein Platzhalter gehört zu Vorschau-Deployments, und nur beschränkt auf dein eigenes Team oder Projekt.
Supabases Anleitung zu Weiterleitungen sagt es klar: Der "Globstar" (**) sei
für die lokale Entwicklung und für Vorschau-Adressen nützlich, in Produktion
empfiehlt sie aber, für deine Site URL den genauen Weiterleitungspfad
festzulegen.
Der Grund ist das, was mit der Person mitreist. Im Standardablauf schreibt
Supabase die neue Sitzung in die Adresse, zu der es sie schickt:
der Code, der sie baut,
hängt #access_token=…&refresh_token=… an die Weiterleitungsadresse. Jede
Seite unter dieser Adresse kann diese beiden Werte lesen, und mit ihnen ist sie
dieser Nutzer. Beim Wegweiser entscheidet der Arm, wer den Schlüssel bekommt.
Ein Eintrag, der auf eine Adresse passt, die jemand anderes kontrolliert, ist also ein Weg in die Konten deiner Nutzer. Jeder kann dein Projekt bitten, einem deiner Nutzer einen Anmeldelink zu mailen, mit dem öffentlichen Schlüssel, der absichtlich in deiner App steht, und seine eigene Seite als Ziel angeben. Die E-Mail, die ankommt, stammt wirklich von deiner App. Der PKCE-Ablauf ändert daran nichts, weil die Person, die diesen Link geschrieben hat, die Anmeldung gestartet hat, und PKCE gibt die Sitzung dem, der sie gestartet hat.
Die Muster, die auf Adressen passen, die andere anlegen können:
**allein passt auf jede Adresse, die es gibt.https://*.vercel.app/**passt auf jedes Vercel-Deployment im Internet, und jeder kann eins anlegen. Supabases eigenes Beispiel enthält dein Team:https://*-<team-or-account-slug>.vercel.app/**.https://*.lovable.app/**,https://*.netlify.app/**und dieselbe Form auf der Domain jedes Builders tun dasselbe für diesen Builder. Supabases Beispiel für Netlify ist ebenfalls beschränkt:https://**--my_org.netlify.app/**.
Wenn du das gerade mit einem eingefügten Platzhalter behoben hast, sieh dir die Liste heute noch einmal an.
Der Start ist auch der Moment, in dem die anderen Einstellungen, die erst in Produktion zählen, angeschaut werden. Unser kostenloser Scan liest deine Live-App von außen und meldet, was ein Fremder erreichen kann: Tabellen, die jedem antworten, geheime Schlüssel im Code, den deine App verschickt, fehlende Header, das Zertifikat. Er dauert etwa 20 Sekunden und braucht kein Konto: scanne deine App.
Die Live-App nach dem Start im Blick behalten
Diese Einstellung wird einmal korrigiert. Was deine App an Besucher schickt, ändert sich mit jedem Deploy, und dieser Teil lässt sich von außen beobachten.
Reeve Monitor prüft deine Live-App weiter:
- ob sie erreichbar ist, alle 60 Sekunden, damit sich "der Link geht nicht" von "die Seite ist weg" unterscheiden lässt
- alle neun Prüfungen jede Stunde, für bis zu drei Apps, einschließlich geheimer Schlüssel im Code, den deine App an Besucher schickt
- eine Nachricht, wenn sich ein Ergebnis ändert, damit dich ein Problem, das mit einem späteren Deploy hinausgeht, erreicht, ohne dass du nachsehen musst
- ein monatlicher Bericht in klarer Sprache über das, was er gesehen hat
Reeve Care hält eine Kopie deiner Supabase-Datenbank, samt Konten:
- eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb deines Supabase-Kontos
- das Auth-Schema, in dem die Konten deiner Nutzer liegen, damit sie mit allem anderen zurückkommen
- jede Kopie geprüft, bevor sie zählt, durch Zählen der Zeilen in jeder Tabelle
- auch deine hochgeladenen Dateien, sobald du einen Storage-Zugang verbindest
- eine Wiederherstellung per Klick, mit einer Sicherheitskopie des aktuellen Stands vorab
- alles, was Monitor tut
Was du jetzt tun solltest
Was zu tun ist
- Setz die Site URL auf der Seite URL Configuration auf deine Live-Adresse,
https://eingeschlossen. - Trag die anderen genauen Adressen, zu denen du Leute schickst, in Redirect URLs ein, etwa die Version mit
wwwund die veröffentlichte Adresse beim Builder. - Sorg dafür, dass jede Adresse, die dein Code als
emailRedirectTooderredirectToübergibt, auf der Liste steht, und nimm{{ .RedirectTo }}in Vorlagen, die ihr folgen sollen. - Sag allen, die nach der Registrierung auf der localhost-Seite gelandet sind, dass ihr Konto bestätigt ist. Magic Links und das Zurücksetzen von Passwörtern brauchen eine neue E-Mail.
- Beschränk Platzhalter auf Vorschauen deines Teams und lass nie jede App auf der Domain eines Builders zu.
Wenn Leute den neuen Link drücken und hören, er sei abgelaufen, hat diese Meldung eine eigene Seite. Die 10-Minuten-Sicherheits-Checkliste deckt ab, was in einer frisch gestarteten App sonst oft offen bleibt.
FAQ
Warum öffnet meine Bestätigungs-E-Mail von Supabase localhost:3000?
Weil Supabase die Person zur Site URL deines Projekts geschickt hat, und die Site URL steht noch auf http://localhost:3000. Supabase nimmt sie immer dann, wenn die Adresse, die deine App verlangt hat, auf der Liste Redirect URLs fehlt oder deine App gar keine verlangt hat. Du änderst sie auf der Seite URL Configuration unter Authentication.
Ist das Konto bestätigt, wenn der Link localhost geöffnet hat?
Ja, bei einer Bestätigung der Registrierung. Supabase bestätigt die Adresse zuerst und schickt den Browser erst danach weiter, die gescheiterte Seite kommt also, wenn die Arbeit schon erledigt ist. Die Person kann sich auf deiner Live-Seite mit dem Passwort anmelden, das sie gewählt hat. Ein Magic Link oder ein Zurücksetzen des Passworts muss neu verschickt werden.
Ich habe meine Seite in Redirect URLs eingetragen, und es geht trotzdem auf localhost. Warum?
Der Eintrag muss Zeichen für Zeichen zu der Adresse passen, die Supabase angeboten wird: mit www oder ohne, mit https oder ohne, und der Pfad, denn ein Eintrag ohne /** am Ende passt nur auf genau diese eine Adresse. Passen sie und der Link übergeht sie trotzdem, baut deine E-Mail-Vorlage ihren Link aus {{ .SiteURL }}. Und korrigier die Site URL selbst, denn auf sie fällt alles andere zurück.
Muss ich meine Site URL in die Liste Redirect URLs eintragen?
Nein. Supabase nimmt jede Adresse auf der eigenen Domain der Site URL ohne Listeneintrag an, unter jedem Pfad. Die Liste ist für die anderen Adressen: eine Version mit www, die veröffentlichte Adresse beim Builder, Vorschau-Deployments.
Darf ich in Supabase Redirect URLs einen Platzhalter benutzen?
Für Vorschauen, beschränkt auf dein eigenes Team oder Projekt, wie in Supabases eigenen Beispielen. Nie einen, der auf Adressen passt, die andere anlegen können, etwa jede App auf vercel.app oder lovable.app. Supabase empfiehlt für deine Seite in Produktion die genaue Adresse.