[{"data":1,"prerenderedAt":1132},["ShallowReactive",2],{"blog-de-stripe-webhook-no-signatures-found":3,"blog-index-de":602},{"id":4,"title":5,"body":6,"category":559,"cover":560,"coverAlt":561,"description":562,"draft":563,"extension":564,"faq":565,"image":581,"keywords":582,"meta":590,"navigation":591,"ogTitle":592,"path":593,"published":594,"seo":595,"stem":596,"tldr":597,"updated":594,"__hash__":601},"blog_de\u002Fblog\u002Fstripe-webhook-no-signatures-found.md","\"No signatures found matching the expected signature\" bei Supabase",{"type":7,"value":8,"toc":547},"minimark",[9,13,24,32,37,40,47,68,71,75,78,84,95,104,110,117,123,126,130,133,136,179,206,209,214,218,226,312,319,323,326,347,353,368,371,375,378,392,399,402,422,434,438,444,453,467,477,494,498,534],[10,11,12],"p",{},"Du hast Stripe mit deiner App verbunden, eine Testzahlung ging durch, und der\nWebhook, der die Bestellung als bezahlt markieren soll, hat nichts getan.\nStripes Dashboard zeigt eine fehlgeschlagene Zustellung, und in den Logs deiner\nSupabase Edge Function steht das hier:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","No signatures found matching the expected signature for payload.\nAre you passing the raw request body you received from Stripe?\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,31],{},"Die gute Nachricht steckt darin, woher der Fehler kommt. Er wird von dem Teil\ndeiner Funktion geworfen, der prüft, ob die Anfrage wirklich von Stripe kam. Die\nPrüfung existiert also und tut ihre Arbeit. ",[28,29,30],"strong",{},"\"No signatures found matching the\nexpected signature for payload\" hat zwei Ursachen, und keine davon behebst du,\nindem du weniger prüfst."," Die Lösung, die bei der Suche zuerst auftaucht, die\nPrüfung so einzupacken, dass der Fehler ignoriert wird, ist genau die, mit der\njeder eine Bestellung als bezahlt markieren kann.",[33,34,36],"h2",{"id":35},"was-bedeutet-no-signatures-found-matching-the-expected-signature","Was bedeutet \"No signatures found matching the expected signature\"?",[10,38,39],{},"Deine Funktion hat Stripes Signatur auf der Anfrage geprüft, und sie passte\nnicht.",[10,41,42,43,46],{},"Stell dir jeden Webhook von Stripe als Brief vor, der mit einem Wachssiegel\nverschlossen ist. Stripe drückt das Siegel mit einem Stempel, den nur Stripe und\ndu besitzen: das Signing Secret deines Endpoints, ein Wert, der mit ",[21,44,45],{},"whsec_","\nbeginnt. Kommt der Brief an, drückt deine Funktion ihre eigene Kopie des\nStempels gegen das Wachs. Passen die Muster, ist der Brief von Stripe, und\nunterwegs hat ihn niemand angefasst.",[10,48,49,50,53,54,61,62,67],{},"Das Siegel deckt den Brief genau so ab, wie er geschrieben wurde. Stripes\nBibliothek nimmt den Text der Anfrage, die Zeit aus dem ",[21,51,52],{},"Stripe-Signature","-Header\nund dein Secret, berechnet, wie die Signatur aussehen müsste, und sucht sie in\ndiesem Header. Die Meldung oben steht\n",[55,56,60],"a",{"href":57,"rel":58},"https:\u002F\u002Fgithub.com\u002Fstripe\u002Fstripe-node\u002Fblob\u002Fmaster\u002Fsrc\u002FWebhooks.ts",[59],"nofollow","in stripe-node selbst","\nund wird geworfen, wenn dort nichts passt. Stripes\n",[55,63,66],{"href":64,"rel":65},"https:\u002F\u002Fdocs.stripe.com\u002Fwebhooks\u002Fsignature",[59],"Seite zur Fehlersuche bei Signaturen","\nsagt es klar: Mindestens einer der drei Werte, die du übergibst, ist falsch.",[10,69,70],{},"Es gibt also zwei Stellen zum Nachsehen. Entweder wurde der Brief verändert,\nbevor deine Funktion das Siegel geprüft hat, oder deine Funktion hält den\nfalschen Stempel.",[33,72,74],{"id":73},"ursache-1-der-body-wurde-vor-der-prüfung-verändert","Ursache 1: Der Body wurde vor der Prüfung verändert",[10,76,77],{},"In einer Edge Function ist die häufigste Variante ein Body, der zuerst als JSON\ngelesen und für die Prüfung wieder in Text verwandelt wurde.",[14,79,82],{"className":80,"code":81,"language":19},[17],"const event = await req.json()\nawait stripe.webhooks.constructEventAsync(JSON.stringify(event), signature, secret, undefined, cryptoProvider)\n",[21,83,81],{"__ignoreMap":23},[10,85,86,87,90,91,94],{},"Das liest sich natürlich und scheitert jedes Mal. ",[21,88,89],{},"req.json()"," behält die Daten\nund wirft den Text weg: die Abstände, die Zeilenumbrüche, die Reihenfolge, in der\nStripe die Dinge geschrieben hat. ",[21,92,93],{},"JSON.stringify"," schreibt dann neuen Text mit\ndenselben Daten darin. Das Siegel wurde über Stripes Text gedrückt, und das hier\nist die Abschrift eines anderen in anderer Handschrift, also kann es nie passen.",[10,96,97,98,103],{},"Supabases eigenes\n",[55,99,102],{"href":100,"rel":101},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase\u002Fblob\u002Fmaster\u002Fexamples\u002Fedge-functions\u002Fsupabase\u002Ffunctions\u002Fstripe-webhooks\u002Findex.ts",[59],"Stripe-Webhook-Beispiel","\nliest den Body stattdessen einmal als Text und gibt diesen String unverändert\nweiter:",[14,105,108],{"className":106,"code":107,"language":19},[17],"const signature = req.headers.get('Stripe-Signature')\nconst body = await req.text()\nconst event = await stripe.webhooks.constructEventAsync(\n  body,\n  signature,\n  Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET'),\n  undefined,\n  cryptoProvider,\n)\n",[21,109,107],{"__ignoreMap":23},[10,111,112,113,116],{},"Der Kommentar in diesem Beispiel sagt, warum: Die Prüfung beruht auf dem rohen\nBody der Anfrage und nicht auf dem geparsten JSON. Selbst parsen musst du danach\nnichts, denn ",[21,114,115],{},"constructEventAsync"," gibt das Event bereits geparst zurück, sobald\ndas Siegel stimmt.",[118,119],"diagram",{"alt":120,"caption":121,"src":122},"Zwei Kopien desselben Webhook-Bodys nebeneinander. Die linke ist der Text genau so, wie er ankam, und ihr Siegel passt zum Stempel, mit Haken. Die rechte enthält dieselben Werte, auf andere Zeilen umgeschrieben, und derselbe Stempel passt nicht mehr, gelb durchgestrichen.","Die Signatur deckt den Text ab, den Stripe gesendet hat, nicht die Daten darin. Umgeschriebener Text mit identischen Daten fällt durch die Prüfung.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fsame-data-other-text-1600x720.png",[10,124,125],{},"Dasselbe passiert außerhalb deines Codes. Stripes Seite nennt Frameworks, die\neinen Body parsen oder umformatieren, bevor dein Code ihn sieht, und die Meldung\nvon stripe-node ergänzt, dass ein Tool, das dir Webhooks weiterleitet, den Body\nexakt erhalten muss, samt JSON-Formatierung und Art der Zeilenumbrüche. Sitzt\nein Proxy oder ein Weiterleitungsdienst zwischen Stripe und deiner Funktion,\nprüfe, dass er den Body genau so weitergibt, wie er kam.",[33,127,129],{"id":128},"ursache-2-das-signing-secret-ist-das-falsche","Ursache 2: Das Signing Secret ist das falsche",[10,131,132],{},"Ist der Body unverändert, ist der Stempel falsch, und Stripes Seite nennt das\ndie häufigste Ursache überhaupt.",[10,134,135],{},"Drei verschiedene Werte können dort landen, wo das Signing Secret hingehört, und\nsie sehen sich ähnlich:",[137,138,139,152,165],"ul",{},[140,141,142,145,146,151],"li",{},[28,143,144],{},"Das Secret eines anderen Endpoints."," Stripes\n",[55,147,150],{"href":148,"rel":149},"https:\u002F\u002Fdocs.stripe.com\u002Fwebhooks",[59],"Webhook-Anleitung"," sagt, dass jeder\nEndpoint sein eigenes Secret bekommt und dass dieselbe Adresse, im Test- und\nim Live-Modus eingerichtet, in jedem Modus ein anderes hat. Eine Live-Zahlung,\ngeprüft mit dem Secret des Test-Endpoints, scheitert, und umgekehrt genauso.",[140,153,154,161,162,164],{},[28,155,156,157,160],{},"Das Secret, das ",[21,158,159],{},"stripe listen"," ausgibt."," Wenn du Events mit Stripes\nKommandozeilen-Tool auf deinen Rechner weiterleitest, gibt es ein eigenes\n",[21,163,45],{},"-Secret aus. Stripe sagt, dass es sich vom Secret des\nDashboard-Endpoints unterscheidet und dass die beiden verwechselt werden. Es\nfunktioniert für die Events, die das Tool weiterleitet, und für nichts sonst.",[140,166,167,170,171,174,175,178],{},[28,168,169],{},"Dein Stripe-API-Schlüssel."," ",[21,172,173],{},"sk_live_…"," oder ",[21,176,177],{},"sk_test_…"," ist der Schlüssel,\nmit dem deine Funktion Stripe aufruft. Er ist kein Signing Secret, und eine\nPrüfung damit kann nie passen.",[10,180,181,182,185,186,189,190,193,194,197,198,201,202,205],{},"Das richtige findest du, indem du den Endpoint in Stripes Dashboard unter\n",[28,183,184],{},"Workbench"," und dann ",[28,187,188],{},"Webhooks"," öffnest und auf ",[28,191,192],{},"Reveal secret"," klickst.\nPrüfe vorher, in welchem Modus du bist. Vergleiche es dann mit dem, das deine\nFunktion liest. Im Beispiel oben ist das ",[21,195,196],{},"STRIPE_WEBHOOK_SIGNING_SECRET",",\ngesetzt auf der Seite ",[28,199,200],{},"Edge Function Secrets"," deines Supabase-Projekts oder\nmit ",[21,203,204],{},"supabase secrets set",".",[10,207,208],{},"Wenn du schon dort bist, sieh dir noch etwas an. Wurde der Wert mit einem\nLeerzeichen oder einem Zeilenumbruch am Ende eingefügt, hängt stripe-node an\ndenselben Fehler einen Hinweis an:\n\"The provided signing secret contains whitespace.\" Lösche den Wert und füge ihn\nneu ein, ohne etwas dahinter.",[118,210],{"alt":211,"caption":212,"src":213},"Links ein versiegelter Umschlag, rechts drei Stempel mit den Beschriftungen whsec_, stripe listen und sk_live_. Nur einer der drei Stempel hat dasselbe Muster wie das Siegel und hat einen Haken. Die anderen beiden, das Secret von stripe listen und ein sk_live_-API-Schlüssel, tragen andere Muster und sind gelb durchgestrichen.","Jeder Endpoint, jeder Modus und das Kommandozeilen-Tool signieren mit einem eigenen Secret. Nur das, mit dem dieses Event signiert wurde, kann es prüfen.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fwhich-stamp-1600x720.png",[33,215,217],{"id":216},"meldungen-die-ähnlich-aussehen-und-etwas-anderes-bedeuten","Meldungen, die ähnlich aussehen und etwas anderes bedeuten",[10,219,220,221,225],{},"stripe-node wirft aus derselben Prüfung mehrere Fehler, und jeder zeigt auf ein\nanderes Problem. Alle stehen\n",[55,222,224],{"href":57,"rel":223},[59],"in seinem Webhook-Code",":",[227,228,229,242],"table",{},[230,231,232],"thead",{},[233,234,235,239],"tr",{},[236,237,238],"th",{},"Was in deinen Logs steht",[236,240,241],{},"Was passiert ist",[243,244,245,256,273,286,296],"tbody",{},[233,246,247,253],{},[248,249,250],"td",{},[21,251,252],{},"No signatures found matching the expected signature for payload",[248,254,255],{},"Der Body wurde verändert, oder das Secret ist das falsche. Dieser Artikel.",[233,257,258,263],{},[248,259,260],{},[21,261,262],{},"Webhook payload must be provided as a string or a Buffer",[248,264,265,266,268,269,272],{},"Du hast das geparste Objekt aus ",[21,267,89],{}," übergeben. Gib den Text aus ",[21,270,271],{},"req.text()"," weiter.",[233,274,275,280],{},[248,276,277],{},[21,278,279],{},"No stripe-signature header value was provided.",[248,281,282,283,285],{},"Die Anfrage hatte keinen ",[21,284,52],{},"-Header. Vielleicht kam sie gar nicht von Stripe.",[233,287,288,293],{},[248,289,290],{},[21,291,292],{},"Timestamp outside the tolerance zone",[248,294,295],{},"Das Siegel passte, aber das Event ist älter als das Limit. Stripes Bibliotheken nehmen 5 Minuten an.",[233,297,298,303],{},[248,299,300],{},[21,301,302],{},"SubtleCryptoProvider cannot be used in a synchronous context.",[248,304,305,306,309,310,205],{},"Du hast ",[21,307,308],{},"constructEvent"," aufgerufen. Mit dem Web-Crypto-Provider, den Deno braucht, ",[21,311,115],{},[10,313,314,315,318],{},"Und eine, die deinen Code nie erreicht. Zeigt Stripes Dashboard die Zustellung\nmit einem ",[28,316,317],{},"401"," als fehlgeschlagen und sind die Logs deiner Funktion leer, hat\ndie Plattform von Supabase die Anfrage abgewiesen, bevor deine Funktion lief,\nwegen einer Prüfung, die bei einem Webhook aus sein muss.",[33,320,322],{"id":321},"warum-dein-webhook-verify_jwt-abgeschaltet-braucht","Warum dein Webhook verify_jwt abgeschaltet braucht",[10,324,325],{},"Weil Stripe nicht so beweisen kann, wer es ist, wie es die Nutzer deiner App\ntun, und bei eingeschalteter Prüfung jede Zustellung abgewiesen wird.",[10,327,328,329,332,333,336,337,342,343,346],{},"Supabase stellt vor jede Edge Function eine Prüfung namens ",[21,330,331],{},"verify_jwt",",\nstandardmäßig eingeschaltet. Sie verlangt ein Supabase-Token im\n",[21,334,335],{},"Authorization","-Header der Anfrage, und Stripe hat keins, das es schicken\nkönnte. Supabases\n",[55,338,341],{"href":339,"rel":340},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Ffunctions\u002Ffunction-configuration",[59],"Anleitung zur Funktionskonfiguration","\nnimmt genau das als Beispiel: Stripe-Webhooks müssen öffentlich erreichbar sein.\nSie schaltet die Prüfung für diese eine Funktion in ",[21,344,345],{},"supabase\u002Fconfig.toml"," ab:",[14,348,351],{"className":349,"code":350,"language":19},[17],"[functions.stripe-webhook]\nverify_jwt = false\n",[21,352,350],{"__ignoreMap":23},[10,354,355,356,359,360,363,364,367],{},"Der Befehl ",[21,357,358],{},"supabase functions deploy"," nimmt ",[21,361,362],{},"--no-verify-jwt"," für dasselbe.\nSupabases Stripe-Beispiel trägt dieselbe Anweisung in einem Kommentar: Stripe\nprüft die Anfrage über ihre Signatur, also mit ",[21,365,366],{},"verify_jwt = false"," deployen.",[10,369,370],{},"Bei abgeschalteter Plattform-Prüfung erreicht jede Anfrage aus dem Internet\ndeinen Code, und die Siegelprüfung in deiner Funktion ist das Einzige, was\nStripes Briefe von denen aller anderen unterscheidet.",[33,372,374],{"id":373},"warum-du-die-signaturprüfung-nie-überspringen-solltest","Warum du die Signaturprüfung nie überspringen solltest",[10,376,377],{},"Weil ohne sie jeder deiner App erzählen kann, dass eine Zahlung durchging.",[10,379,380,381,384,385,388,389,391],{},"Die Adresse einer Webhook-Funktion ist die URL deines Projekts, die für jeden\nlesbar in deinem Frontend steht, gefolgt von ",[21,382,383],{},"\u002Ffunctions\u002Fv1\u002F"," und dem Namen der\nFunktion, meist dem naheliegenden. Das Beispiel der Konfigurationsanleitung\nnennt sie selbst ",[21,386,387],{},"stripe-webhook",". Mit abgeschaltetem ",[21,390,331],{}," lässt die\nPlattform jede Anfrage zu deinem Code durch. Prüft dein Code das Siegel nicht\nmehr, glaubt er jedem Brief, der ankommt.",[10,393,394,395,398],{},"Also schickt ein Fremder deiner Funktion eine Anfrage mit\n",[21,396,397],{},"checkout.session.completed"," und einer Bestellung oder einem Kunden seiner Wahl\ndarin. Deine Funktion tut, was sie mit einer echten tut: markiert die\nBestellung als bezahlt, stuft das Konto hoch, gibt den Download frei. Bezahlt\nhat niemand. Stripe hat keinen Eintrag dazu, weil Stripe sie nie gesendet hat.",[10,400,401],{},"Drei Arten, die Prüfung zu überspringen, enden alle dort:",[137,403,404,410,416],{},[140,405,406,409],{},[28,407,408],{},"Sie löschen",", weil sie nicht durchging.",[140,411,412,415],{},[28,413,414],{},"Den Fehler abfangen und weitermachen",", also das Scheitern loggen und das\nEvent trotzdem verarbeiten. Das Beispiel gibt in seinem catch-Block einen 400\nzurück, und diese Form solltest du behalten: Eine Anfrage, die durch die\nPrüfung fällt, endet dort.",[140,417,418,421],{},[28,419,420],{},"Die Zeittoleranz auf 0 setzen."," Stripes Webhook-Anleitung rät davon ab:\n0 schaltet die Altersprüfung ganz ab, sodass ein echtes Event, das jemand\nabgefangen hat, später erneut gesendet werden kann.",[10,423,424,425,429,430,205],{},"Ein aktiver geheimer Stripe-Schlüssel in deinem Frontend ist der andere Weg, auf\ndem Zahlungen schiefgehen, und den sehen wir von außen: Wir fanden einen in\n",[55,426,428],{"href":427},"\u002Fblog\u002Fstripe-secret-key-in-frontend","3 von 30.998 gescannten Apps",". Unser\nkostenloser Scan liest das JavaScript deiner Live-App so, wie ein Fremder es\nlesen würde, und nennt jeden Schlüssel, den er findet, samt der Angabe, ob er\ndorthin gehört. Das dauert etwa 20 Sekunden und braucht kein Konto:\n",[55,431,433],{"href":432},"\u002Fsecurity-scanner","scanne deine App",[33,435,437],{"id":436},"falls-je-eine-gefälschte-zahlung-durchkommt","Falls je eine gefälschte Zahlung durchkommt",[10,439,440,443],{},[28,441,442],{},"Eine Prüfung, die ein Prompt entfernt hat, kündigt sich nicht an."," Ein\nBuilder, den du bittest, den Webhook wieder zum Laufen zu bringen, darf die\nAbkürzung oben nehmen, und deine App läuft dabei weiter.",[10,445,446],{},[28,447,448,452],{},[55,449,451],{"href":450},"\u002Fwebsite-monitoring","Reeve Monitor"," beobachtet, was ein Fremder von außen sehen kann:",[137,454,455,458,461,464],{},[140,456,457],{},"alle neun Checks jede Stunde, für bis zu drei Apps",[140,459,460],{},"eine E-Mail an dem Tag, an dem ein erneuter Scan deine App schlechter benotet als der vorige; ein geheimer Schlüssel in deinem JavaScript deckelt die Note allein schon bei D",[140,462,463],{},"ob die App erreichbar ist, alle 60 Sekunden",[140,465,466],{},"ein monatlicher Bericht darüber, was gesehen wurde",[10,468,469],{},[28,470,471,472,476],{},"Ein gefälschtes Event schreibt in deine Datenbank wie ein echtes.\n",[55,473,475],{"href":474},"\u002Fsupabase-backups","Reeve Care"," hält eine Kopie deiner Supabase-Datenbank von\ndavor.",[137,478,479,482,485,488,491],{},[140,480,481],{},"eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb deines Supabase-Kontos",[140,483,484],{},"jede Kopie wird geprüft, bevor sie zählt, durch Zählen der Zeilen in jeder Tabelle",[140,486,487],{},"eine Wiederherstellung per Klick, die sichert, was jetzt da ist, bevor sie es ersetzt",[140,489,490],{},"deine hochgeladenen Dateien ebenfalls, sobald du eine Storage-Zugangsberechtigung verbindest",[140,492,493],{},"alles, was Monitor tut",[33,495,497],{"id":496},"was-jetzt-zu-tun-ist","Was jetzt zu tun ist",[499,500,501],"key-takeaways",{},[137,502,503,506,519,525,531],{},[140,504,505],{},"Öffne die fehlgeschlagene Zustellung in Stripes Dashboard und den Logs-Tab deiner Funktion und lies die genaue Meldung, bevor du irgendetwas änderst.",[140,507,508,509,512,513,515,516,518],{},"Lies den Body mit ",[21,510,511],{},"await req.text()"," und gib diesen String unverändert an ",[21,514,115],{}," weiter. Ein Event nie mit ",[21,517,93],{}," prüfen.",[140,520,521,522,524],{},"Kopiere das ",[21,523,45],{},"-Secret des Endpoints, von dem Stripe sendet, im richtigen Modus, und füge es ohne etwas dahinter in Edge Function Secrets ein.",[140,526,527,528,530],{},"Lass ",[21,529,366],{}," auf dem Webhook und die Signaturprüfung als Erstes, was die Funktion tut.",[140,532,533],{},"Gib einen 400 zurück, wenn die Prüfung scheitert. Den Fehler nie abfangen und das Event trotzdem verarbeiten.",[10,535,536,537,541,542,546],{},"Was eine Funktion, die ein Secret hält, sonst noch braucht, steht in\n",[55,538,540],{"href":539},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","ein Secret in eine Edge Function verschieben",",\nund wenn du den allgemeinen Fehler von supabase-js hast, zeigt\n",[55,543,545],{"href":544},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","einen Non-2xx-Statuscode lesen",",\nwo die echte Meldung steht.",{"title":23,"searchDepth":548,"depth":548,"links":549},3,[550,552,553,554,555,556,557,558],{"id":35,"depth":551,"text":36},2,{"id":73,"depth":551,"text":74},{"id":128,"depth":551,"text":129},{"id":216,"depth":551,"text":217},{"id":321,"depth":551,"text":322},{"id":373,"depth":551,"text":374},{"id":436,"depth":551,"text":437},{"id":496,"depth":551,"text":497},"Sicherheitsgrundlagen","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcover-1200x630.png","Ein verschlossener Umschlag mit türkisem Wachssiegel, daneben ein Siegelstempel, dessen Muster nicht zum Abdruck passt, gelb markiert.","Ein Stripe-Webhook in einer Supabase Edge Function meldet \"No signatures found matching the expected signature\"? Zwei Ursachen. Prüfe weiter.",false,"md",[566,569,572,575,578],{"q":567,"a":568},"Was bedeutet \"No signatures found matching the expected signature for payload\"?","Deine Webhook-Funktion hat aus dem Body der Anfrage und deinem Signing Secret die Signatur berechnet, die sie erwartet, und keine der Signaturen im Stripe-Signature-Header passte dazu. Stripes Bibliothek wirft das, wenn der Body, den sie bekommt, nicht Byte für Byte dem entspricht, was Stripe gesendet hat, oder wenn das Secret nicht zu dem Endpoint gehört, der das Event geschickt hat.",{"q":570,"a":571},"Warum scheitert es, wenn ich JSON.stringify des Events übergebe?","Weil die Signatur genau den Text abdeckt, den Stripe gesendet hat, mit Leerzeichen und Zeilenumbrüchen. Liest du den Body mit req.json() und machst mit JSON.stringify wieder Text daraus, hast du dieselben Daten in anderem Text, und anderer Text hat eine andere Signatur. Lies den Body einmal mit await req.text() und gib diesen String an constructEventAsync weiter.",{"q":573,"a":574},"Welches Signing Secret nehme ich?","Das whsec_-Secret genau des Endpoints, an den Stripe liefert. Jeder Endpoint hat sein eigenes, dieselbe Adresse hat im Test- und im Live-Modus jeweils ein anderes, und der Befehl stripe listen gibt ein eigenes Secret aus, das nur für die Events gilt, die er weiterleitet. Dein sk_-API-Schlüssel ist überhaupt kein Signing Secret.",{"q":576,"a":577},"Soll meine Stripe-Webhook-Funktion verify_jwt abgeschaltet haben?","Ja. Stripe hat kein Supabase-Token, das es mitschicken könnte, also wird bei eingeschalteter Prüfung jede Zustellung mit einem 401 abgewiesen, bevor dein Code läuft. Supabases eigenes Stripe-Beispiel sagt, man solle es mit verify_jwt = false deployen. Damit ist die Signaturprüfung in deiner Funktion die einzige Prüfung überhaupt, und sie darf nie übersprungen oder abgefangen und ignoriert werden.",{"q":579,"a":580},"Kann ich die Signaturprüfung entfernen, damit Zahlungen funktionieren?","Kannst du, und dann kann jeder, der die Adresse der Funktion findet, ihr ein Event schicken, das eine erfolgreiche Zahlung meldet. Die Adresse ist deine Projekt-URL, die in deinem Frontend steht, plus der Name der Funktion. Ohne die Prüfung glaubt deine Funktion jeder Anfrage und gewährt, was laut Event bezahlt wurde.","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",[583,584,585,586,587,588,589],"no signatures found matching the expected signature for payload","stripe webhook supabase edge function","stripe signature verification failed","constructEventAsync deno","stripe webhook raw body","stripe webhook signing secret","supabase stripe webhook 401",{},true,"\"No signatures found matching the expected signature\"","\u002Fblog\u002Fstripe-webhook-no-signatures-found","2026-10-29",{"title":5,"description":562},"blog\u002Fstripe-webhook-no-signatures-found",[598,599,600],"\"No signatures found matching the expected signature for payload\" heißt: Deine Funktion hat Stripes Signatur geprüft, und sie passte nicht. Entweder wurde der Body vor der Prüfung verändert, oder das Signing Secret ist nicht das, mit dem Stripe signiert hat.","Lies den Body mit await req.text() und gib genau diesen String unverändert an constructEventAsync weiter. Vergleiche dann das whsec_-Secret deiner Funktion mit dem des Endpoints, von dem Stripe tatsächlich sendet.","Lass den Fehler nicht verschwinden, indem du die Prüfung weglässt. Der Webhook muss mit verify_jwt = false laufen, also ist die Signatur das Einzige, was einen Fremden davon abhält, deiner App eine erfolgreiche Zahlung zu melden.","HxDoXCI0SGxgB54eYGv4i3YV4cq8dOAiPXuqlNf0zvo",[603,609,615,621,627,633,639,645,651,657,663,670,671,677,682,688,694,700,706,712,718,724,730,736,742,748,754,760,766,772,778,784,790,796,802,808,813,819,825,831,837,843,849,855,861,867,872,878,884,890,896,902,908,913,919,925,931,937,943,949,955,961,967,973,979,985,991,997,1003,1009,1015,1021,1027,1033,1039,1045,1051,1057,1063,1069,1075,1081,1087,1093,1098,1104,1110,1116,1121,1127],{"path":604,"title":605,"description":606,"published":607,"category":559,"image":608,"draft":563},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\" erklärt","Supabase \"max client connections reached\": Der Pooler hat keinen Platz für noch einen Client. Der Port in deinem Connection String setzt das Limit.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":610,"title":611,"description":612,"published":613,"category":559,"image":614,"draft":563},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Supabase-Fehler: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\" heißt: Google-Login ist in deinem Supabase-Projekt aus. Die Lösung, und was bei redirect_uri_mismatch hilft.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":616,"title":617,"description":618,"published":619,"category":559,"image":620,"draft":563},"\u002Fblog\u002Fviolates-foreign-key-constraint","Supabase-Fehler \"violates foreign key constraint\" beim Einfügen","Supabase \"insert or update on table violates foreign key constraint\": Deine Zeile zeigt auf eine, die es nicht gibt. Welche, warum, und die Lösung.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":622,"title":623,"description":624,"published":625,"category":559,"image":626,"draft":563},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Supabase-Fehler \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": Eine Pflichtspalte kam leer an. Woher die Null kommt, und die Lösung.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":628,"title":629,"description":630,"published":631,"category":559,"image":632,"draft":563},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": wo die Sitzung geblieben ist","\"Auth session missing!\" heißt: supabase-js fand keine angemeldete Sitzung und fragte Supabase gar nicht erst. Wo die Sitzung blieb, und die Lösung je Fall.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":634,"title":635,"description":636,"published":637,"category":559,"image":638,"draft":563},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": niemand ist angemeldet","\"invalid claim: missing sub claim\" heißt: Supabase Auth bekam ein echtes Token ohne Nutzer darin, deinen Projektschlüssel statt einer Sitzung.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":640,"title":641,"description":642,"published":643,"category":559,"image":644,"draft":563},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": der echte Grund","\"Failed to send a request to the Edge Function\" heißt: Der Browser hat keine lesbare Antwort bekommen. Seine Konsole sagt, warum, und ob deine Funktion lief.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":646,"title":647,"description":648,"published":649,"category":559,"image":650,"draft":563},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","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.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":652,"title":653,"description":654,"published":655,"category":559,"image":656,"draft":563},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" bei einem neuen Link","Bei einem Link von vor einer Minute heißt Supabases \"Email link is invalid or has expired\" meist: Ein Mailfilter hat ihn zuerst benutzt. Prüfen und beheben.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":658,"title":659,"description":660,"published":661,"category":559,"image":662,"draft":563},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\" erklärt","Supabase \"canceling statement due to statement timeout\": Eine Abfrage lief länger als ihre Rolle darf, 3 Sekunden für Besucher. Prüf zuerst die RLS-Regel.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":664,"title":665,"description":666,"published":667,"category":668,"image":669,"draft":563},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" in Supabase und was mitgelöscht wird","\"Database error deleting user\" in Supabase heißt: Deine Datenbank hat das Löschen abgelehnt. Der Grund steht im Log, die Lösung entscheidet, was mitgeht.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":593,"title":5,"description":562,"published":594,"category":559,"image":581,"draft":563},{"path":672,"title":673,"description":674,"published":675,"category":559,"image":676,"draft":563},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" in Supabase: die drei Ursachen","Supabase sagt \"Invalid login credentials\" bei falschem Passwort, unbekannter E-Mail und Google-Konto. So erkennst du, welcher Fall es ist.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":544,"title":678,"description":679,"published":680,"category":559,"image":681,"draft":563},"\"Edge Function returned a non-2xx status code\": der echte Fehler","\"Edge Function returned a non-2xx status code\" heißt: Deine Supabase-Funktion hat mit einem Fehler geantwortet. So liest du den echten.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":683,"title":684,"description":685,"published":686,"category":559,"image":687,"draft":563},"\u002Fblog\u002Fcertificate-not-trusted","Zertifikat nicht vertrauenswürdig: drei Ursachen außer Ablauf","Zertifikat nicht vertrauenswürdig, obwohl das Datum stimmt? Es nennt eine andere Adresse, hat sich selbst signiert oder seiner Kette fehlt ein Glied.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":689,"title":690,"description":691,"published":692,"category":559,"image":693,"draft":563},"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","Supabase \"invalid input syntax for type uuid\": was im Zitat steht","Supabase \"invalid input syntax for type uuid\" heißt: Deine App hat etwas geschickt, das keine ID ist. Zitat lesen, Abfrage zurückhalten, Spalte behalten.","2026-10-25","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",{"path":695,"title":696,"description":697,"published":698,"category":559,"image":699,"draft":563},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\" erklärt","Supabase \"duplicate key value violates unique constraint\" heißt: Der Wert ist schon vergeben. Spalte finden, Lösung wählen, Wert vom Bildschirm fernhalten.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":701,"title":702,"description":703,"published":704,"category":559,"image":705,"draft":563},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\", obwohl die Tabelle da ist","Supabase \"relation does not exist\" oder PGRST205: Keine Tabelle passt zu Name und Schema deiner Abfrage. Vier Prüfungen, und was eine Freigabe öffnet.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":707,"title":708,"description":709,"published":710,"category":559,"image":711,"draft":563},"\u002Fblog\u002Fsupabase-function-not-found","Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" nennt die Argumente, die deine App geschickt hat. Vergleich sie mit der Funktion.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":713,"title":714,"description":715,"published":716,"category":559,"image":717,"draft":563},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" heißt meist: Der Name passt nicht. Vier Prüfungen und der Einzeiler zum Neuladen.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":719,"title":720,"description":721,"published":722,"category":559,"image":723,"draft":563},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": zwei Limits, ein Fehlercode","Supabase \"Email Rate Limit Exceeded\" begrenzt jede E-Mail deines Projekts: 2 pro Stunde beim eingebauten Absender. Was zählt und wie du es anhebst.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":725,"title":726,"description":727,"published":728,"category":559,"image":729,"draft":563},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 Ursachen","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" meldet deine Nutzer ab. Drei Ursachen, und wie das Auth-Log sie trennt.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":731,"title":732,"description":733,"published":734,"category":559,"image":735,"draft":563},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": drei Ursachen","Supabase \"permission denied for schema public\" heißt: Ein Grant auf das Schema fehlt. Was ihn entfernt hat und welche Zeile ihn zurückbringt.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":737,"title":738,"description":739,"published":740,"category":559,"image":741,"draft":563},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": die drei Dinge, die es bedeutet","Supabase \"Bucket not found\" ist eins von drei Dingen: ein öffentlicher Link zu einem privaten Bucket, ein ungenauer Name oder das falsche Projekt.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":743,"title":744,"description":745,"published":746,"category":559,"image":747,"draft":563},"\u002Fblog\u002Fsupabase-invalid-jwt","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.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":749,"title":750,"description":751,"published":752,"category":559,"image":753,"draft":563},"\u002Fblog\u002Fsupabase-single-row-error","Supabase \"multiple (or no) rows returned\" heißt meist: keine Zeilen","Supabase gibt PGRST116 zurück, \"JSON object requested, multiple (or no) rows returned\", wenn die Abfrage nichts fand. Versteckte Zeilen wirken fehlend.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":755,"title":756,"description":757,"published":758,"category":559,"image":759,"draft":563},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\" in Supabase: wo du nachsiehst","\"Database error saving new user\" heißt: etwas an deiner Supabase-Registrierung ist fehlgeschlagen. Der echte Fehler steht in deinen Logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":761,"title":762,"description":763,"published":764,"category":559,"image":765,"draft":563},"\u002Fblog\u002Fwhat-is-supabase","Was ist Supabase? Die Datenbank, die dein Builder ausgesucht hat","Was ist Supabase: die Datenbank, der Login und der Dateispeicher hinter deiner App. Supabase betreibt die Server, die Regeln zu deinen Zeilen gehören dir.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":767,"title":768,"description":769,"published":770,"category":559,"image":771,"draft":563},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Ein offengelegter AWS-Schlüssel im Frontend ist kein OpenAI-Key","Ein im Frontend offengelegter AWS-Zugriffsschlüssel liest und schreibt deinen Speicher. Die Lösung ist meist eine presigned URL, kein neuer Schlüssel.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":773,"title":774,"description":775,"published":776,"category":559,"image":777,"draft":563},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" beim Upload","\"New row violates row-level security policy for table objects\" heißt: Deinem Upload fehlt eine insert-Regel. Ein öffentlicher Bucket fügt keine hinzu.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":779,"title":780,"description":781,"published":782,"category":559,"image":783,"draft":563},"\u002Fblog\u002Fbase44-security-scan","Base44 Security Scan: das, was nur er sehen kann","Der Base44 Security Scan prüft sieben Arten von Problemen von innen. Hier ist die Hälfte, die er liest und sonst niemand, und die Hälfte, die er nie ansieht.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":785,"title":786,"description":787,"published":788,"category":559,"image":789,"draft":563},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Dein API-Key wurde geleakt. Die richtige Reihenfolge","Ein API-Key wurde geleakt und du weißt nicht, was zuerst zu tun ist. Nicht jeder Key im Frontend ist ein Leck, und die Reihenfolge zählt mehr als das Tempo.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":791,"title":792,"description":793,"published":794,"category":559,"image":795,"draft":563},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" ohne RLS abzuschalten","\"Infinite recursion detected in policy for relation\" heißt: Deine Policy hat die Tabelle gefragt, die sie schützt. So brichst du den Kreis.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":797,"title":798,"description":799,"published":800,"category":559,"image":801,"draft":563},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" in Supabase, und die falsche Lösung","\"No API key found in request\" heißt, deine Supabase-Anfrage kam ohne Schlüssel an. Die meisten Antworten zeigen stattdessen auf deine Datenbankregeln.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":803,"title":804,"description":805,"published":806,"category":559,"image":807,"draft":563},"\u002Fblog\u002Fis-base44-safe","Ist Base44 sicher? Was 5.442 echte Base44-Apps zeigten","Ist Base44 sicher? Wir haben 5.442 laufende Base44-Apps mit neun Checks geprüft. Fast alle bekamen ein B, aus Gründen, die der Inhaber nie gewählt hat.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":539,"title":809,"description":810,"published":811,"category":559,"image":812,"draft":563},"API-Schlüssel verstecken: ab in eine Supabase Edge Function","Einen API-Schlüssel verstecken heißt, ihn aus dem Browser zu nehmen. Eine Supabase Edge Function ist der kleinste Ort dafür, und zwei Schritte zählen mehr.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":814,"title":815,"description":816,"published":817,"category":559,"image":818,"draft":563},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Ist deine .env-Datei öffentlich? Zwölf Pfade zum Prüfen","Ist deine .env-Datei auf deinem eigenen Webserver öffentlich? Zwölf Adressen sagen es dir, und ein Treffer heißt: alles darin ist längst öffentlich.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":820,"title":821,"description":822,"published":823,"category":559,"image":824,"draft":563},"\u002Fblog\u002Fis-v0-safe","v0-Sicherheit: alle 1.790 gescannten v0-Apps bekamen ein A","v0-Sicherheit, gemessen an 1.790 laufenden v0-Apps: Jede bekam ein A. Nur 17 nannten eine Datenbank, und genau das misst das A vor allem.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":826,"title":827,"description":828,"published":829,"category":559,"image":830,"draft":563},"\u002Fblog\u002Fis-bolt-safe","Ist Bolt sicher? Was 1.123 echte Bolt-Apps zeigten","Ist Bolt sicher? Wir haben 1.123 laufende Bolt-Apps mit neun Checks geprüft. Das Hosting war sauber. Die Funde waren Schlüssel und offene Tabellen in den Apps.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":832,"title":833,"description":834,"published":835,"category":559,"image":836,"draft":563},"\u002Fblog\u002Fis-supabase-down-or-your-app","Ist Supabase down oder liegt es an deiner App? So erkennst du es","Ist Supabase down oder liegt es an deiner App? Ein pausiertes Projekt, eine volle Datenbank und ein eigener Fehler sehen gleich aus. Vier Prüfungen helfen.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":838,"title":839,"description":840,"published":841,"category":559,"image":842,"draft":563},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Verschlüsselt Supabase meine Daten? Ja. Wovor das schützt","Verschlüsselt Supabase Daten? Ja: AES-256 im Ruhezustand, TLS bei der Übertragung, SOC 2 und ISO 27001. Was jedes abdeckt und welches Leck keines stoppt.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":844,"title":845,"description":846,"published":847,"category":559,"image":848,"draft":563},"\u002Fblog\u002Fsupabase-free-plan-limits","Supabase Gratis-Tarif: die Limits und was an jedem passiert","Die Limits des Supabase-Gratis-Tarifs: 500 MB Datenbank, 5 GB Egress und keine Backups. Was an jeder Grenze passiert und was $25 für Pro ändern.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":850,"title":851,"description":852,"published":853,"category":668,"image":854,"draft":563},"\u002Fblog\u002Ftest-your-supabase-backup","Teste dein Supabase-Backup, bevor du es brauchst","So testest du dein Supabase-Backup: in ein Übungsprojekt einspielen, Zeilen zählen, anmelden und die Zeile suchen, die einer abgeschnittenen Datei fehlt.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":856,"title":857,"description":858,"published":859,"category":668,"image":860,"draft":563},"\u002Fblog\u002Fsupabase-backup-github-action","Kostenloses Supabase-Backup per GitHub Action, mit Haken","Eine Supabase-Backup-GitHub-Action kostet nichts und passt für viele Apps. Der Workflow, der richtige Connection String und das Egress pro Lauf.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":862,"title":863,"description":864,"published":865,"category":668,"image":866,"draft":563},"\u002Fblog\u002Fdownload-your-supabase-backup","Warum du dein Supabase-Backup nicht herunterladen kannst","Dein Supabase-Backup kannst du auf aktuellen Projekten nicht herunterladen, weil die Tageskopie physisch ist. Wie du es prüfst und selbst eine Kopie hältst.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":868,"title":869,"description":870,"published":865,"category":559,"image":871,"draft":563},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": der fehlende Grant","Ab 30. Oktober antwortet eine neue Supabase-Tabelle mit \"permission denied for table\", bis du Zugriff erteilst. Der Grant aus der E-Mail ist die halbe Lösung.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":873,"title":874,"description":875,"published":876,"category":559,"image":877,"draft":563},"\u002Fblog\u002Fopen-api-endpoint-exposed","Ist ein offener API-Endpunkt ein Sicherheitsproblem? Sieh ins JSON","Dein Scan hat einen offenen API-Endpunkt gemeldet. Ob das zählt, hängt davon ab, was zurückkam, und die meisten hier gehörten der Plattform.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":559,"image":883,"draft":563},"\u002Fblog\u002Flovable-security-scan","Lovable Security Scan: das Eine, was er nicht beweist","Lovable Security Scan: was Quick Scan und Deep Scan prüfen, wann welcher läuft, und das Eine, was kein Scan von innen beweisen kann.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":668,"image":889,"draft":563},"\u002Fblog\u002Fsupabase-backup-auth-users","Warum in deinem Supabase-Dump keine Nutzer stehen","Führe supabase db dump allein aus und du bekommst die Form deiner Datenbank und keine einzige Zeile, und das auth-Schema mit deinen Nutzern fehlt ganz.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":891,"title":892,"description":893,"published":894,"category":559,"image":895,"draft":563},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domain abgelaufen, Website offline: was jetzt passiert","Deine Domain ist abgelaufen und deine Website ist offline. Welche Frist jetzt läuft, warum das Zertifikat das kleinere Problem ist, und wie du beides prüfst.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":897,"title":898,"description":899,"published":900,"category":559,"image":901,"draft":563},"\u002Fblog\u002Fis-lovable-safe","Ist Lovable sicher? Was 18.554 echte Lovable-Apps zeigten","Ist Lovable sicher? Wir haben 18.554 laufende Lovable-Apps mit neun Checks geprüft. Die Plattform war die sauberste von fünf. Die Funde saßen in den Apps.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":903,"title":904,"description":905,"published":906,"category":559,"image":907,"draft":563},"\u002Fblog\u002Fvibe-coded-app-security-checklist","Die Sicherheits-Checkliste für Vibe-Coding, in neun Prüfungen","Eine Sicherheits-Checkliste für Vibe-Coding mit neun Punkten, die jeder von außen an deiner Live-App nachprüfen kann, jeder mit einem Test in einer Zeile.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":427,"title":909,"description":910,"published":911,"category":559,"image":912,"draft":563},"Ein geheimer Stripe-Schlüssel im Frontend kann Geld bewegen","Ein geheimer Stripe-Schlüssel im Frontend kann erstatten, abbuchen und jeden Kundendatensatz lesen, den du hast. Dein pk_live_-Schlüssel gehört dorthin.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":914,"title":915,"description":916,"published":917,"category":559,"image":918,"draft":563},"\u002Fblog\u002Fvite-and-next-public-env-vars","Vite-Env-Variablen offengelegt: der Präfix heißt veröffentlichen","Vite-Env-Variablen offengelegt: Deine App hat getan, was der Präfix verlangt. VITE_ und NEXT_PUBLIC_ heißen veröffentlichen, und die KI kannte den Preis nie.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":920,"title":921,"description":922,"published":923,"category":559,"image":924,"draft":563},"\u002Fblog\u002Fbase44-source-maps","Base44-Sicherheit: was ein Scan meldet und was du ändern kannst","Base44-Sicherheit auf 5.438 gescannten Apps: Drei Befunde auf fast jeder gehören der Plattform, die Source Map ist das Base44-Badge, und deine Liste ist kurz.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":926,"title":927,"description":928,"published":929,"category":559,"image":930,"draft":563},"\u002Fblog\u002Fis-cursor-ai-safe","Ist Cursor AI sicher? Editor, Code und die App, die du veröffentlichst","Ist Cursor AI sicher? Drei Fragen in einer Suche: was Cursor behält, was sein Code falsch macht und ob deine veröffentlichte App offen steht.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":932,"title":933,"description":934,"published":935,"category":559,"image":936,"draft":563},"\u002Fblog\u002Fis-replit-safe","Ist Replit sicher? Was wir in 3.042 Replit-Apps gefunden haben","Ist Replit sicher? Wir haben 3.042 laufende Replit-Apps mit neun externen Checks geprüft. Die Funde saßen in der veröffentlichten App, nicht im Hosting.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":938,"title":939,"description":940,"published":941,"category":668,"image":942,"draft":563},"\u002Fblog\u002Fsupabase-storage-backup","Supabase Storage Backup: deine Datenbankkopie hat keine Dateien","Ein Supabase Storage Backup ist eine eigene Aufgabe. Datenbank-Backups enthalten die Liste deiner Dateien und keine davon, also bleibt jeder Upload kaputt.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":944,"title":945,"description":946,"published":947,"category":668,"image":948,"draft":563},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Supabase Point-in-Time Recovery: Kosten und Grenzen","Supabase Point-in-Time Recovery spult deine Datenbank auf jede Sekunde der letzten Woche zurück. $100 im Monat zusätzlich zu Pro, und nur die Datenbank.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":950,"title":951,"description":952,"published":953,"category":668,"image":954,"draft":563},"\u002Fblog\u002Fsupabase-project-paused-recover","Supabase-Projekt pausiert? Deine Daten sind noch da","Supabase hat dein Projekt nach einer Woche ohne Aktivität pausiert. Gelöscht ist nichts, Wiederherstellen steht neben dem Namen, und du hast ein Jahr.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":956,"title":957,"description":958,"published":959,"category":559,"image":960,"draft":563},"\u002Fblog\u002Fsafest-ai-app-builder","Welcher KI-App-Builder ist am sichersten? 30.998 Apps im Test","Welcher KI-App-Builder ist am sichersten? Wir haben 30.998 aktive Apps von Lovable, Base44, Replit, v0 und Bolt gescannt. Der Builder entscheidet das nicht.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":962,"title":963,"description":964,"published":965,"category":559,"image":966,"draft":563},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Row Level Security für jede Supabase-Tabelle aktivieren und prüfen","Row Level Security in Supabase ohne Policy sperrt eine Tabelle komplett. Eine Policy ohne den Schalter bringt gar nichts. Hier ist das SQL, und der Test.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":968,"title":969,"description":970,"published":971,"category":559,"image":972,"draft":563},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": was die Warnung übersieht","Supabase meldet \"RLS disabled in public\" als Fehler. Zur Lese-Policy, die deine Tabelle genauso offen lässt, sagt der Advisor gar nichts.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":974,"title":975,"description":976,"published":977,"category":559,"image":978,"draft":563},"\u002Fblog\u002Frotate-supabase-service-role-key","Geleakten Supabase-service_role-Schlüssel richtig tauschen","Supabase sagt: erst das Leck schließen. Andere Anleitungen: sofort tauschen. Was richtig ist, hängt davon ab, wohin dein service_role-Schlüssel gelangt ist.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":980,"title":981,"description":982,"published":983,"category":559,"image":984,"draft":563},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Vibe-Coding-Security-Scanner im Vergleich, unserer eingeschlossen","Der beste Vibe-Coding-Security-Scanner hängt an drei Fragen, die keine Funktionsliste beantwortet. Acht Tools im Vergleich, mit Preisen und unseren Lücken.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":986,"title":987,"description":988,"published":989,"category":559,"image":990,"draft":563},"\u002Fblog\u002Fsupabase-security-checker","Supabase Security Checker: die fünf Prüfungen selbst machen","Ein Supabase Security Checker liest deine veröffentlichte App statt deiner Projekteinstellungen. Hier sind die fünf Prüfungen und wie du jede selbst ausführst.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":992,"title":993,"description":994,"published":995,"category":559,"image":996,"draft":563},"\u002Fblog\u002Fvibe-coding-security-scanner","Vibe-Coding-Security-Scanner: was ein URL-Scan übersieht","Ein Vibe-Coding-Security-Scanner liest deine laufende App von außen. Was das abdeckt, die vier Dinge, die er nicht sehen kann, und wie du das Ergebnis liest.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":998,"title":999,"description":1000,"published":1001,"category":559,"image":1002,"draft":563},"\u002Fblog\u002Fmissing-security-headers","Fehlende Sicherheitsheader: wann sie wirklich zählen","Fehlende Sicherheitsheader ist unser häufigster Befund. Wogegen sie schützen, und wann sie die unwichtigste Zeile in deinem Bericht sind.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1004,"title":1005,"description":1006,"published":1007,"category":559,"image":1008,"draft":563},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Dein OpenAI-API-Schlüssel liegt im Frontend. Tausch ihn aus.","Ein OpenAI-API-Schlüssel im Frontend lässt sich an keine Domain binden. Tausch ihn heute, leg den Aufruf hinter deinen Server und begrenze das Budget.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1010,"title":1011,"description":1012,"published":1013,"category":559,"image":1014,"draft":563},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","API-Schlüssel im Frontend: was 30.998 Apps wirklich ausliefern","Ein API-Schlüssel im Frontend ist meistens ein Google-Maps-Schlüssel. Wir haben 30.998 aktive Vibe-Coding-Apps gescannt und gezählt, was wirklich austritt.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1016,"title":1017,"description":1018,"published":1019,"category":668,"image":1020,"draft":563},"\u002Fblog\u002Fsupabase-backup-tools-compared","Supabase-Backup-Tools im Vergleich, unseres eingeschlossen","Vier Arten von Supabase-Backup-Tool, was jedes wirklich kopiert und wann eine kostenlose GitHub Action jeden Anbieter schlägt, uns eingeschlossen.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1022,"title":1023,"description":1024,"published":1025,"category":559,"image":1026,"draft":563},"\u002Fblog\u002Freplit-secrets-explained","Secrets in Replit nutzen, und was trotzdem veröffentlicht wird","Secrets in Replit nutzen: anlegen, auslesen und die zwei Gründe beheben, warum undefined zurückkommt. Dazu die Schlüssel, die das Werkzeug nicht schützt.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1028,"title":1029,"description":1030,"published":1031,"category":668,"image":1032,"draft":563},"\u002Fblog\u002Fback-up-supabase-free-tier","Supabase Gratis-Tarif: Backup ohne Terminal selbst machen","Im Supabase Gratis-Tarif gibt es keine Backups, die Kopie musst du selbst machen. Wie das im Dashboard geht und was der CSV-Weg auslässt.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1034,"title":1035,"description":1036,"published":1037,"category":559,"image":1038,"draft":563},"\u002Fblog\u002Fis-supabase-secure","Ist Supabase sicher? Ja. Dein Projekt ist eine eigene Frage","Ist Supabase sicher? Die Plattform ist auditiert, verschlüsselt und pentestet. Supabase schreibt selbst auf, wo das aufhört und deine Einstellungen anfangen.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1040,"title":1041,"description":1042,"published":1043,"category":559,"image":1044,"draft":563},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Wo du deine Supabase API-Keys findest: anon, service_role, URL","Projekt-URL, anon-Schlüssel und service_role-Schlüssel liegen auf einer Seite im Dashboard. Wo sie ist, und welcher der vier Werte in deine App gehört.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1046,"title":1047,"description":1048,"published":1049,"category":559,"image":1050,"draft":563},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy in Supabase: was jetzt?","\"New row violates row-level security policy\" heißt, Supabase hat einen Schreibvorgang abgelehnt. Die schnelle Lösung öffnet die Tabelle wieder für alle.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1052,"title":1053,"description":1054,"published":1055,"category":559,"image":1056,"draft":563},"\u002Fblog\u002Fcors-wildcard-security-risk","Ist eine CORS-Wildcard ein Sicherheitsrisiko? Meist nicht.","Ist eine CORS-Wildcard ein Sicherheitsrisiko? Meist ist sie die Voreinstellung deines Builders und gibt nichts preis, was dein Server nicht ohnehin herausgab.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1058,"title":1059,"description":1060,"published":1061,"category":668,"image":1062,"draft":563},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase Branching ist kein Backup. Es geht nur vorwärts.","Warum Supabase Branching kein Backup ist: Ein Branch startet ohne deine Daten, und ein Merge bringt nur das Schema. Wofür es gut ist und was stattdessen hilft.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1064,"title":1065,"description":1066,"published":1067,"category":668,"image":1068,"draft":563},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Supabase-Backup wiederherstellen, und was danach kaputt ist","Wie du ein Supabase-Backup aus dem Dashboard oder aus einer Dump-Datei wiederherstellst, was ersetzt wird und warum die App danach kaputt sein kann.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1070,"title":1071,"description":1072,"published":1073,"category":559,"image":1074,"draft":563},"\u002Fblog\u002Fsupabase-storage-bucket-public","Dein Supabase-Storage-Bucket ist öffentlich. Ist das ein Problem?","Ein öffentlicher Supabase-Storage-Bucket heißt: Wer die URL einer Datei hat, kann sie öffnen. Er heißt nicht, dass jemand den Inhalt auflisten darf.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1076,"title":1077,"description":1078,"published":1079,"category":559,"image":1080,"draft":563},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Ist ein Google-API-Schlüssel im Frontend ein Problem?","Ein Google-API-Schlüssel im Frontend ist der Fund unseres Scanners Nummer eins, und meistens unbedenklich. Eine kostenlose Einstellung entscheidet darüber.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1082,"title":1083,"description":1084,"published":1085,"category":668,"image":1086,"draft":563},"\u002Fblog\u002Fai-agent-deleted-my-database","Ein KI-Agent hat meine Supabase-Daten gelöscht. Was hole ich zurück?","Ein KI-Agent hat Daten aus deiner Datenbank gelöscht. Was du zurückholst, stand schon vorher fest, und die nächsten Minuten entscheiden über den Rest.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1088,"title":1089,"description":1090,"published":1091,"category":559,"image":1092,"draft":563},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Kann jeder deine Supabase-Datenbank lesen? 3.680 Apps im Test","Kann jeder deine Supabase-Datenbank ohne Login lesen? Wir haben 30.998 aktive Apps aus KI-Buildern gescannt und gemessen, wie oft die Antwort ja lautet.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1094,"title":1095,"description":1096,"published":1091,"category":559,"image":1097,"draft":563},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source-Maps öffentlich: deine App veröffentlicht ihren Quellcode","Eine öffentliche Source-Map lässt jeden den Originalcode deiner App lesen, Kommentare inklusive. Der 30-Sekunden-Check, und was wirklich zählt.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1099,"title":1100,"description":1101,"published":1102,"category":559,"image":1103,"draft":563},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ist er derselbe wie der anon key?","Supabase hat anon und service_role durch öffentliche und geheime Schlüssel ersetzt. Welcher gehört in deine App, und welcher nie?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1105,"title":1106,"description":1107,"published":1108,"category":668,"image":1109,"draft":563},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Sichert Supabase meine Datenbank? Das hängt von deinem Tarif ab.","Sichert Supabase deine Datenbank? Auf bezahlten Tarifen täglich, im Gratis-Tarif gar nicht. So findest du heraus, was du hast und was es nicht abdeckt.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1111,"title":1112,"description":1113,"published":1114,"category":559,"image":1115,"draft":563},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security ist an. Die Tabelle ist trotzdem offen.","Supabase Row Level Security anzuschalten schützt keine Tabelle. Erst deine Policies tun das, und die, die deine App repariert hat, lässt oft alle herein.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1117,"title":1118,"description":1119,"published":1114,"category":668,"image":1120,"draft":563},"\u002Fblog\u002Fversion-history-is-not-a-backup","Der Versionsverlauf ist kein Backup. Er holt keine Tabelle zurück.","Lovable und Bolt führen einen Versionsverlauf für deinen Code. Deine Datenbank ist ein eigener Dienst, und ein Rücksprung holt deine Daten nicht zurück.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1122,"title":1123,"description":1124,"published":1125,"category":668,"image":1126,"draft":563},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Drei Wege, eine Supabase-Datenbank zu sichern, und was fehlt","Das Dashboard, pg_dump und ein verwalteter Dienst. Was jeder Weg wirklich sichert, was er stillschweigend auslässt und welcher den Kontoverlust übersteht.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1128,"title":1129,"description":1130,"published":1125,"category":559,"image":1131,"draft":563},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Welche API-Schlüssel im Frontend sicher sind, und welche nicht","Dein Supabase-anon-Schlüssel darf öffentlich sein. Der service_role-Schlüssel nicht. Er ignoriert jede Regel, die du setzt. So erkennst du den Unterschied.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213638699]