Sicherheitsgrundlagen
Supabase invalid JWT: Welcher Schlüssel streikt hier?
Ein Supabase invalid JWT ist PGRST301: ein signiertes Token hat die Prüfung nicht bestanden. In neueren Projekten ist das die Sitzung, nicht der API-Schlüssel.

Kurz gesagt
- Ein Supabase invalid JWT kommt als PGRST301 mit HTTP 401 an, und es bedeutet, dass die Signatur des mitgeschickten Tokens nicht geprüft werden konnte. Es wurde nichts nachgeschlagen und abgelehnt.
- Publishable und Secret Keys sind kurze Zeichenketten ohne Signatur. In einem Projekt, das darauf umgestellt hat, geht es bei diesem Fehler also um Ihren angemeldeten Nutzer und nie um den API-Schlüssel.
- PostgREST 13 hat einen vagen Satz durch eine kurze Liste konkreter Sätze ersetzt. Lesen Sie die Meldung, die Sie tatsächlich bekommen haben: jede schließt etwas anderes aus.
Gestern lief Ihre App. Heute Morgen kommt auf jede Anfrage eine 401 zurück, an Ihrem Code hat sich nichts geändert, und in der Antwort steht etwas von einem JWT, das Sie nie irgendwo eingetippt haben.
{
"code": "PGRST301",
"message": "JWT cryptographic operation failed"
}
Ein Supabase invalid JWT ist der Fehler, der auftaucht, nachdem Sie alles richtig gemacht haben. Sie haben einen Schlüssel gewechselt, weil eine Anleitung das geraten hat, oder Ihr Projekt auf die neueren Schlüssel umgestellt, und dann war Ruhe.
Und hier liegen fast alle Anleitungen daneben: der Schlüssel in dieser Meldung
ist oft nicht der, den Sie gerade gewechselt haben. Suchen Sie den Fehler, und
man schickt Sie zu Ihrem anon-Schlüssel. In einem Projekt, das auf Publishable
und Secret Keys umgestellt hat, ist der API-Schlüssel überhaupt kein signiertes
Token, also ist das Geprüfte etwas anderes in Ihrer Anfrage gewesen.
Was bedeutet ein Supabase invalid JWT?
Irgendetwas in Ihrer Anfrage war ein signiertes Token, und die Signatur hat nicht gepasst.
Ein Passwort ist ein Wort, das jemand auf einer Liste nachschlägt. Ein JWT ist das nicht. Es ist ein Brief, der mit einem Wachssiegel verschlossen wurde: der Mensch an der Tür sucht Sie nirgends heraus, er hält einen Siegelstempel in der Hand und prüft, ob das Siegel auf Ihrem Brief damit gemacht wurde. Wenn ja, gehen Sie durch, und niemand liest eine Liste.
Ein Brief mit dem falschen Siegel wird abgewiesen, obwohl jedes Wort darin stimmt. Und sobald die Tür ihren Siegelstempel wechselt, lässt sich kein einziger Brief mehr öffnen, der schon unterwegs ist, ohne dass jemand die Briefe angefasst hätte.
Ihre Anfrage ist der Brief. Ihr Supabase-Projekt hält den Siegelstempel. Eine 401 heißt hier, dass die beiden nicht zusammenpassten.
In PostgREST 13 hat sich die Meldung geändert, der Code nicht
Prüfen Sie auf PGRST301. Welcher Satz danebensteht, hängt davon ab, welche
PostgREST-Version Ihr Projekt fährt, und die neueren geben Ihnen eine kurze Liste
davon.
Die Fehlerreferenz von PostgREST beschreibt PGRST301 sowohl in Version 13 als
auch in Version 14 als ein mitgeschicktes JWT, das nicht dekodiert werden konnte
oder ungültig ist, bei HTTP 401. Dieser Satz ist die Beschreibung in der
Dokumentationstabelle. Er ist nicht das, was in Ihrem message-Feld landet. In
Error.hs auf dem main-Branch von PostgREST steht, dass dieses Feld einen dieser
Sätze trägt:
| Was in der Meldung steht | Was Ihnen das sagt |
|---|---|
Empty JWT is sent in Authorization header | Der Header wurde mitgeschickt und war leer. |
Expected 3 parts in JWT; got 1 | Was da ankam, ist kein JWT. Ein JWT besteht aus drei durch Punkte getrennten Blöcken. |
No suitable key or wrong key type | Der Server hat keinen Schlüssel, mit dem sich ein Token dieser Art prüfen ließe. |
Wrong or unsupported encoding algorithm | Das Token nennt ein Signaturverfahren, das der Server nicht verwendet. |
JWT cryptographic operation failed | Das Token ist korrekt aufgebaut und die Signatur passt nicht. |
Unsupported token type | Das Token hat eine Form, die PostgREST nicht liest. |
JWT cryptographic operation failed ist der Satz, der einen veralteten Schlüssel
meint. Das Token wurde korrekt gebaut und mit etwas signiert, das das Projekt
nicht mehr hat, und genau das bleibt nach einem Schlüsselwechsel zurück.
Ältere Projekte sehen etwas ganz anderes, und daher stammt die eine Zeichenkette,
die überall in den Suchergebnissen steht. Vor Version 13 hat PostgREST genau das
ausgegeben, was seine JWT-Bibliothek gesagt hat, Wort für Wort. Daher kommt
JWSError JWSInvalidSignature: das ist ein ausgegebener Bibliothekswert und kein
Satz, den jemand für Sie geschrieben hat. Das Changelog von PostgREST zu 13.0.0
nennt die Änderung diagnostische Fehlermeldungen statt offengelegter Interna.
Ein Nachbarcode lohnt sich hier noch. PGRST303, ebenfalls 401, bekommen Sie,
wenn die Signatur in Ordnung war und der Inhalt des Tokens abgelehnt wurde:
abgelaufen, noch nicht gültig oder mit der falschen Audience. Wenn das Ihr Code
ist, hören Sie auf, die Schlüssel anzusehen, und schauen Sie auf die Uhr und auf
die Sitzung.
Welcher Schlüssel ist es: Ihr API-Schlüssel oder das Token Ihres Nutzers?
Das hängt davon ab, ob Ihr Projekt noch anon und service_role verwendet.
Die Dokumentation von Supabase zu den API-Schlüsseln sagt den Unterschied
deutlich. Die alten Schlüssel anon und service_role sind langlebige JWTs, und
deshalb beginnen sie mit eyJ. Die neueren Publishable und Secret Keys sind
kurze Zeichenketten und überhaupt keine JWTs.
Diese eine Tatsache teilt den Fehler in zwei Hälften:
- Ihr Projekt verwendet noch
anonundservice_role. Der API-Schlüssel in Ihrer Anfrage ist selbst ein signiertes Token. Ein Schlüssel aus einem anderen Projekt, einer von vor dem Wechsel oder eine abgeschnittene Kopie fallen damit genauso bei der Signaturprüfung durch wie eine kaputte Sitzung. Beide Tokens in der Anfrage kommen infrage. - Ihr Projekt ist auf
sb_publishable_undsb_secret_umgestiegen. Der API-Schlüssel ist damit kein Token mehr, es gibt also keine Signatur daran, die irgendetwas ablehnen könnte. Zu prüfen bleibt das Access Token Ihres angemeldeten Nutzers, das Supabase Auth getrennt vom API-Schlüssel ausstellt.
Praktisch heißt das: die erste Frage ist nicht, welcher Schlüssel falsch ist. Sie lautet, ob jemand angemeldet war, als die Anfrage scheiterte. Wenn eine Seite ohne Anmeldung funktioniert und eine mit Anmeldung nicht, geht es um Sitzungen. Wenn alles scheitert, auch eine Seite, die nichts Persönliches abfragt, geht es um den API-Schlüssel, und Ihr Projekt ist ein altes.
Geht es um Sitzungen und steht in Ihrem Auth-Log außerdem
Invalid Refresh Token, werden Ihre Nutzer abgemeldet, weil
die Sitzung hinter ihrem Token geendet hat.
Kam die Antwort von Supabase Auth und lautet sie invalid claim: missing sub claim, hat das Token seine Signaturprüfung bestanden und niemanden genannt. Dann
wurde ein Schlüssel dort geschickt, wo das Token einer Person hingehört, und
diese Meldung hat einen eigenen Beitrag.
Invalid JWT ist nicht "No API key found in request"
Das sind zwei verschiedene Meldungen mit zwei verschiedenen Ursachen, und die Lösungen haben nichts gemeinsam.
Invalid JWT heißt, ein Token kam an und die Signatur passte nicht. "No API key
found in request" heißt, dass gar kein
apikey-Header ankam, die Anfrage also am Gateway abgewiesen wurde, bevor Ihre
Datenbank überhaupt gefragt wurde.
Die Antwort selbst klärt, was Sie vor sich haben. Ein fehlender Schlüssel wird
vom Gateway vor Ihrem Projekt abgewiesen, der Body trägt also ein message und
einen hint und überhaupt keinen code. Ein invalid JWT ist weit genug
gekommen, um von PostgREST gelesen zu werden, und deshalb steht PGRST301 darauf.
Wo der alte Schlüssel noch liegt, der Reihe nach
Vier Stellen, und sie sind in dieser Reihenfolge zu prüfen, weil das ungefähr der Häufigkeit entspricht, mit der sie die Antwort sind.
- Die Projekteinstellungen Ihres Baukastens. Lovable, Bolt, v0 und Replit führen jeweils eigene Umgebungsvariablen, und wenn Sie mit Cursor oder Claude Code gebaut haben, liegen dieselben Werte dort, wo Sie deployen. Das ist die Kopie, die die meisten Leute ändern, und die einzige, die die meisten Leute ändern.
- Ein direkt in den Code geschriebener Schlüssel. Er liegt in dem JavaScript, das Ihre Besucher herunterladen, eine Änderung im Dashboard ändert also nichts, bis die Datei bearbeitet und die Seite neu gebaut wurde. Wo diese Seite ist und was jeder Wert darauf bedeutet, falls Sie sie noch nicht geöffnet haben.
- Edge-Function-Secrets. Eine Function hat ihren eigenen Satz, getrennt von dem Ihres Frontends, und verwendet den alten Schlüssel weiter, wenn alles andere längst umgezogen ist. Supabase weist außerdem darauf hin, dass eine Function mit eingeschalteter Einstellung Verify JWT beim Rotieren eines Signing Key kaputtgehen kann.
- Ein Deployment, das niemand neu gebaut hat. Der Schlüssel wurde beim letzten Build fest eingebacken, die laufende Kopie schickt ihn also weiter, bis irgendetwas einen neuen Build auslöst. Ihr Dashboard zeigt dabei die ganze Zeit den neuen Schlüssel, und das macht diesen Fall so schwer zu sehen: die Stelle, an der Sie nachsehen, ist die eine, die schon stimmt.
Welchen Schlüssel Ihre Live-App wirklich verschickt
Öffnen Sie die veröffentlichte Seite statt der Vorschau und lesen Sie, was die Seite mitschickt.
Ein alter Supabase-Schlüssel ist eine lange Zeichenkette, die mit eyJ beginnt.
Die neueren sagen es in ihren ersten Zeichen: sb_publishable_ gehört in Ihre
App und sb_secret_ niemals. Wenn Sie den zweiten auf einer Seite finden, die
ein Besucher laden kann, hören Sie hier auf zu lesen und
wechseln Sie ihn, bevor Sie irgendetwas
anderes tun. Welchen der beiden Sie haben und
was sich geändert hat, als Supabase sie umbenannt
hat, sollten Sie klären, bevor Sie anfangen, Werte
zu tauschen.
Um zu sehen, was Ihr Projekt derzeit ausstellt: alle Schlüssel liegen inzwischen an einer Stelle im Dashboard, unter Settings und dann API Keys, die alten eingeschlossen.
Wenn Sie lieber kein minifiziertes Bundle lesen möchten: unser kostenloser Scan liest Ihre Live-App so, wie ein Fremder sie sieht, und nennt die Schlüssel, die von außen sichtbar sind. Das dauert etwa 20 Sekunden und braucht kein Konto: App scannen.
Einen Signing Key rotieren, ohne alle abzumelden
Auf welchem der beiden Wege Sie unterwegs sind, entscheidet, ob Ihre Nutzer etwas davon merken.
Die Supabase-Dokumentation zu Signing Keys stellt beide nebeneinander. Wer das
alte JWT-Secret rotiert, meldet alle gerade aktiven Nutzer sofort ab, und
anon und service_role müssen gleichzeitig rotiert werden. Die neueren JWT
Signing Keys funktionieren anders: ein neuer Schlüssel startet als Standby, mit
dem Rotieren wird er der aktuelle, und der abgelöste wird zum zuvor verwendeten.
Solange das so ist, werden Signaturen von beiden Schlüsseln weiter akzeptiert,
letzte Woche gesiegelte Briefe lassen sich also noch öffnen. Erst das Widerrufen
des alten Schlüssels ist der Schritt, der das beendet.
Zwei Warnungen in derselben Dokumentation sind die, über die Leute stolpern:
- Eine Komponente, die Tokens selbst gegen das alte JWT-Secret prüft, etwa mit einer Bibliothek wie jose oder jsonwebtoken, bleibt beim Rotieren stehen. Sie hält den alten Siegelstempel und niemand hat ihr gesagt, dass sie ihn wechseln soll.
- Eine Edge Function mit eingeschalteter Einstellung Verify JWT kann aus demselben Grund kaputtgehen.
Publishable und Secret Keys anzulegen widerruft Ihre alten Schlüssel ebenfalls nicht. Sie funktionieren weiter, bis Sie sie deaktivieren, was die Dokumentation als eigenen Schritt ausweist. Eine App, die nur die Hälfte ihrer Kopien umgestellt hat, läuft deshalb wochenlang weiter, und an dem Tag, an dem jemand die alten Schlüssel abschaltet, fängt die andere Hälfte an, 401 zu antworten.
Eine App im Blick behalten, die anfängt, 401 zu antworten
Eine App, die auf jede Anfrage 401 zurückgibt, ist weiterhin erreichbar, liefert weiterhin eine Seite aus und beantwortet weiterhin jeden Healthcheck. In Ihrem Dashboard wird nichts rot. Sie erfahren es, wenn Ihnen jemand schreibt.
Reeve Monitor erfährt es zuerst:
- alle neun Prüfungen jede Stunde, für bis zu drei Apps
- eine E-Mail an dem Tag, an dem ein erneuter Scan Ihre App schlechter bewertet als der vorige
- ob die App erreichbar ist, alle 60 Sekunden
- ein Monatsbericht über das Gesehene
Ein Schlüssel, den Sie in Eile wechseln mussten, war meist ein Schlüssel, der irgendwo lag, wo er nicht hingehörte, und von hier aus sehen Sie nicht, was damit gelesen oder geändert wurde, solange er draußen war. Reeve Care hält eine Kopie Ihrer Supabase-Datenbank von davor bereit.
- eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb Ihres Supabase-Kontos
- jede Kopie wird geprüft, bevor sie zählt, indem die Zeilen jeder Tabelle gezählt werden
- eine Wiederherstellung per Klick, die den aktuellen Stand sichert, bevor sie ihn ersetzt
- auf Wunsch auch Ihre hochgeladenen Dateien, sobald Sie eine Storage-Zugangsberechtigung hinterlegen
- alles, was Monitor kann
Was heute zu tun ist
Was zu tun ist
- Lesen Sie das Feld
code, nicht den Satz.PGRST301ist eine Signatur, die nicht passte,PGRST303ein Token, dessen Inhalt abgelehnt wurde, etwa ein abgelaufenes. - Klären Sie, welches Token durchgefallen ist, bevor Sie einen Schlüssel ändern. Wenn Seiten ohne Anmeldung laufen und Seiten mit Anmeldung nicht, ist es eine Sitzung und nicht Ihr API-Schlüssel.
- Prüfen Sie alle vier Stellen, an denen der alte Schlüssel liegt: Baukasteneinstellungen, der Code selbst, Edge-Function-Secrets und ein Deployment, das niemand neu gebaut hat.
- Ziehen Sie einen Wechsel in einem Zug durch. Jede zurückgelassene Kopie ist eine App, die halb läuft, und die laufende Seite schickt den alten Schlüssel weiter, bis sie neu gebaut wird.
- Bei den neueren Signing Keys ist das Widerrufen der Schritt, der die alten Signaturen wirklich beendet. Rotieren allein lässt sie gültig.
Fangen Sie mit der Frage an, ob eine Seite ohne Anmeldung noch lädt, denn diese eine Antwort entscheidet, welche Hälfte dieses Artikels für Sie gilt. Falls ein Secret Key offen herumlag, beschreibt ihn zu wechseln die richtige Reihenfolge, und welche Schlüssel je sicher auslieferbar waren erklärt, warum sich die beiden überhaupt so ähnlich sehen.
FAQ
Was bedeutet PGRST301?
PGRST301 ist die Antwort von PostgREST mit HTTP 401, weil ein signiertes Token in Ihrer Anfrage nicht verifiziert werden konnte. PostgREST ist der Server, der Ihre Supabase-Tabellen in die HTTP-API verwandelt, mit der Ihre App spricht. Seine eigene Fehlerreferenz beschreibt den Code als ein mitgeschicktes JWT, das nicht dekodiert werden konnte oder ungültig ist, und ab Version 13 steht im Feld message ein genauerer Satz dazu, welcher Teil der Prüfung fehlgeschlagen ist.
Warum bekomme ich 401, nachdem ich meinen Supabase-Schlüssel gewechselt habe?
Weil irgendwo noch eine Kopie des alten Schlüssels verschickt wird. Die üblichen Stellen sind die Projekteinstellungen Ihres Baukastens, ein direkt in den Code geschriebener Schlüssel, ein Edge-Function-Secret und ein Deployment, das vor der Änderung gebaut und seitdem nicht neu gebaut wurde. Ein Token, das mit einem Schlüssel signiert ist, den der Server nicht mehr akzeptiert, besteht die Prüfung nicht, und das kommt bei Ihnen als 401 auf jede Anfrage an statt als Hinweis auf den Schlüssel.
Was ist der Unterschied zwischen invalid JWT und No API key found in request?
Das sind zwei verschiedene Meldungen mit verschiedenen Ursachen. No API key found in request bedeutet, dass die Anfrage ganz ohne apikey-Header ankam und am Gateway abgewiesen wurde, bevor Ihre Datenbank überhaupt gefragt wurde. Invalid JWT bedeutet, dass ein Token da war und seine Signatur nicht gepasst hat. Das eine ist eine Anfrage ohne Adresse, das andere eine Anfrage mit dem falschen Siegel.
Muss ich meine Edge Functions auch anpassen?
Ja, und das ist die Kopie, die am häufigsten vergessen wird, denn eine Edge Function hat ihre eigenen Secrets, getrennt von denen Ihres Frontends. Supabase weist außerdem darauf hin, dass eine Function mit eingeschalteter Einstellung Verify JWT beim Rotieren eines Signing Key kaputtgehen kann, und dass jede Komponente, die Tokens selbst gegen das alte JWT-Secret prüft, aus demselben Grund stehen bleibt.
Wie sehe ich, welchen Schlüssel meine Live-App wirklich verschickt?
Öffnen Sie Ihre veröffentlichte Seite, nicht die Vorschau, und schauen Sie, was die Seite tatsächlich mitschickt. Ein alter Supabase-Schlüssel ist eine lange Zeichenkette, die mit eyJ beginnt, die neueren beginnen mit sb_publishable_ oder sb_secret_. Unser kostenloser Scan liest eine Live-App so, wie ein Fremder sie sieht, und nennt die Schlüssel, die von außen sichtbar sind. Damit sehen Sie auch, ob noch ein alter mitgeschickt wird.