[{"data":1,"prerenderedAt":728},["ShallowReactive",2],{"blog-en-stripe-secret-key-in-frontend":3,"blog-index-en":485},{"id":4,"title":5,"body":6,"category":438,"cover":439,"coverAlt":440,"description":441,"draft":442,"extension":443,"faq":444,"image":463,"keywords":464,"meta":472,"navigation":473,"ogTitle":474,"path":475,"published":476,"seo":477,"stem":478,"tldr":479,"updated":476,"__hash__":484},"blog_en\u002Fblog\u002Fstripe-secret-key-in-frontend.md","A Stripe secret key exposed in your frontend can move money",{"type":7,"value":8,"toc":426},"minimark",[9,18,33,42,47,55,58,63,68,71,75,78,182,193,203,207,210,213,219,226,229,232,236,239,273,276,281,284,287,291,294,301,304,307,311,323,332,337,340,357,361,394,397,404],[10,11,12,13,17],"p",{},"Open your live app in a browser, view the page source, and search it for\n",[14,15,16],"code",{},"sk_live_",". If a long string comes back, a Stripe secret key is exposed in your\nfrontend, and every visitor you have ever had could have copied it.",[10,19,20,21,25,26,29,30,32],{},"Here is the part that general advice about API keys gets wrong: Stripe hands you\ntwo live keys, they look almost identical, and ",[22,23,24],"strong",{},"one of them is supposed to be\nin your app."," The key beginning ",[14,27,28],{},"pk_live_"," belongs there. The key beginning\n",[14,31,16],{}," has, in Stripe's own words, unrestricted permissions on all APIs.\nThey differ by one character in the middle of a long string, which is most of\nthe reason this keeps happening.",[10,34,35,36,41],{},"Between 12 and 14 August 2026 we ran nine external checks over 30,998 live apps\nbuilt with Lovable, Bolt, v0, Replit and Base44. Three of them were shipping a\nlive Stripe secret key, and two more were shipping a restricted one, which makes\nthis among the rarest things the sweep turned up; the same nine checks found a\nGoogle API key in 1,142 apps. All three of the secret keys came back graded D,\nbecause a single critical finding caps the grade there no matter what else the\napp got right. The full numbers are in\n",[37,38,40],"a",{"href":39},"\u002Fresearch\u002Fvibe-coded-app-security-2026","our scan report",".",[43,44,46],"h2",{"id":45},"is-a-stripe-secret-key-exposed-in-your-frontend-a-problem","Is a Stripe secret key exposed in your frontend a problem?",[10,48,49,50,52,53,41],{},"Yes, if the string begins ",[14,51,16],{},". No, if it begins ",[14,54,28],{},[10,56,57],{},"Stripe issues two live keys because the two halves of a payment happen in two\ndifferent places. Picture a shop counter. The card reader faces the customer and\nis bolted down in full view, and the worst a stranger can do with it is pay you.\nThe till behind the counter is a different object entirely. It opens, it holds\nthe day's takings, and the drawer under it holds a card for every customer with\ntheir address written on it.",[10,59,60,62],{},[14,61,28],{}," is the card reader. Its job is to build a payment form inside\nsomebody else's browser, and Stripe's documentation says publishable keys are\nsafe to expose in front-end code.",[10,64,65,67],{},[14,66,16],{}," is the till key. Stripe describes secret keys as having unrestricted\npermissions on all APIs, which is the same sentence read from the other side:\nthere is nothing in your account it cannot reach.",[10,69,70],{},"Your app needs the card reader in the browser to take a payment at all. It\nshould never need the till key.",[43,72,74],{"id":73},"pk_live_-and-sk_live_-how-to-tell-them-apart","pk_live_ and sk_live_: how to tell them apart",[10,76,77],{},"Read the second character of the prefix. That is the whole test.",[79,80,81,100],"table",{},[82,83,84],"thead",{},[85,86,87,91,94,97],"tr",{},[88,89,90],"th",{},"Key",[88,92,93],{},"Starts with",[88,95,96],{},"Safe in the browser?",[88,98,99],{},"What it does",[101,102,103,124,143,160],"tbody",{},[85,104,105,109,114,121],{},[106,107,108],"td",{},"Publishable",[106,110,111],{},[14,112,113],{},"pk_live_…",[106,115,116],{},[117,118,120],"key-verdict",{"type":119},"safe","Belongs here",[106,122,123],{},"Builds the payment form and tokenises a card. Cannot read customers or move money.",[85,125,126,129,134,140],{},[106,127,128],{},"Secret",[106,130,131],{},[14,132,133],{},"sk_live_…",[106,135,136],{},[117,137,139],{"type":138},"danger","Never",[106,141,142],{},"Unrestricted permissions on all of Stripe's APIs, across your whole account.",[85,144,145,148,153,157],{},[106,146,147],{},"Restricted",[106,149,150],{},[14,151,152],{},"rk_live_…",[106,154,155],{},[117,156,139],{"type":138},[106,158,159],{},"Only the permissions you ticked when you made it. Still a working credential in a stranger's hands.",[85,161,162,165,174,179],{},[106,163,164],{},"Test keys",[106,166,167,170,171],{},[14,168,169],{},"sk_test_",", ",[14,172,173],{},"pk_test_",[106,175,176],{},[117,177,178],{"type":138},"No",[106,180,181],{},"Touches your sandbox only. A smaller problem with the same habit behind it.",[10,183,184,185,188,189,192],{},"The middle of the prefix is the second thing to read. ",[14,186,187],{},"_test_"," reaches nothing\nbut your sandbox, so a leaked test key costs you no money; it still publishes\nhow your integration is built, and Stripe's guidance is to treat any secret or\nrestricted key seen where it should not be as compromised. Newer accounts can\nalso carry an organisation key beginning ",[14,190,191],{},"sk_org_",", which operates across more\nthan one Stripe account at once. It follows the same rule, and a leak of one\nreaches further than a single account.",[194,195,197],"callout",{"type":196},"warn",[10,198,199,202],{},[22,200,201],{},"Nothing breaks when you paste the wrong one."," A checkout built with the\nsecret key works. Payments arrive, the dashboard fills up, and the app behaves\nexactly as it would have done with the publishable key, because the secret key\nis allowed to do everything the publishable one can and more besides. There is\nno error to notice and no warning to dismiss, which is why this finding tends to\nbe months old by the time anybody reads it off a scan.",[43,204,206],{"id":205},"what-someone-can-do-with-a-leaked-stripe-secret-key","What someone can do with a leaked Stripe secret key",[10,208,209],{},"Everything you can do in your own dashboard that does not need your password.",[10,211,212],{},"Not \"gain unauthorised access\". Concretely, with nothing but the string and a\nterminal: read your full customer list, with names, email addresses, billing\naddresses and the last four digits of every card. Read every payment you have\never taken, and what each customer paid for. Issue refunds. Create charges and\npayment links in your name. Cancel subscriptions.",[214,215],"diagram",{"alt":216,"caption":217,"src":218},"A grid with four rows of icons down the left: a card terminal, three people, two stacked coins, and a stack of table rows. Two columns are headed pk_live_ and sk_live_. In the pk_live_ column the card terminal row has a green tick and the other three rows have faint crosses. In the sk_live_ column all four rows have red ticks.","Both keys sit in the same file. The second column is what the extra character buys, and every red tick in it is a request that succeeds.","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fwhat-each-key-can-do-1600x820.png",[10,220,221,222,225],{},"Then there is the use that has nothing to do with you. Your account becomes a\nplace to run ",[22,223,224],{},"card testing",", which is Stripe's own term for a fraudster\npushing stolen card numbers through somebody's integration to find the ones that\nstill work. The cards belong to other people. The declines, the disputes and the\nexplaining belong to you.",[10,227,228],{},"What they mostly cannot do is pay themselves. A refund goes back to the card\nthat made the original payment, and a payout goes to the bank account on file,\nwhich is yours. That sounds like good news and is not: it means the damage\narrives as your money leaving, your customers' records copied, and your account\nused for someone else's fraud, rather than as a transfer you could point at and\nchase.",[10,230,231],{},"None of this needs a sophisticated attacker. Stripe's own documentation says\nfraudulent actors continuously scan public codebases for exposed keys, and those\nscanners do not need to know who you are to find yours.",[43,233,235],{"id":234},"how-to-roll-a-stripe-secret-key-without-breaking-live-payments","How to roll a Stripe secret key without breaking live payments",[10,237,238],{},"Create the replacement first, put it live, then expire the old one. In that\norder.",[240,241,242,249,255,261,267],"ol",{},[243,244,245,248],"li",{},[22,246,247],{},"In the Stripe Dashboard, create a new secret key."," Leave the old one alone for now; both work at once, and that overlap is what keeps your checkout running.",[243,250,251,254],{},[22,252,253],{},"Put the new key where the old one lived",", which should be a server, an Edge Function or a serverless route. Never the app the browser downloads.",[243,256,257,260],{},[22,258,259],{},"Deploy, then take one real payment."," A live charge that succeeds is the only proof the new key is wired up correctly.",[243,262,263,266],{},[22,264,265],{},"Expire the old key."," Stripe describes this exactly: expiring a secret or restricted key prevents it from making any further API calls.",[243,268,269,272],{},[22,270,271],{},"Read your payment history"," for the period the key was exposed, and your Stripe email for anything you did not do.",[10,274,275],{},"If the key is already out in public and you would rather lose a few payments\nthan leave it live for another hour, do it the other way round. Rolling a key\nblocks it immediately and generates a new one, and Stripe notes that webhook\nendpoints created with the old key stay active, so your event handling survives\nthe emergency.",[214,277],{"alt":278,"caption":279,"src":280},"Three numbered panels left to right. In panel one an app pane connects to a single key chip. In panel two a second key chip has appeared, the app now connects to it, and the first chip is drawn faint with no line to it. In panel three the first chip is crossed out and gone, leaving the app connected to the new chip alone.","The overlap in the middle panel is the whole point. Both keys are live at once, which is what lets the swap happen without a gap in your checkout.","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Frolling-the-key-1600x620.png",[10,282,283],{},"There is one more step, and it is the one most people do first: deleting the key\nfrom your code. Do it, and be clear what it does. Your visitors already downloaded the\nfile that carried it, that file is in browser caches you do not control, and the\nold value is still in your version history. Expiring the key at Stripe is what\ncloses the door. Removing the line stops you shipping it again.",[10,285,286],{},"Publishable keys, for what it is worth, cannot be expired at all. Stripe never\nbuilt the control, because that key was never meant to be private.",[43,288,290],{"id":289},"what-a-stripe-restricted-key-is-and-when-it-is-the-right-answer","What a Stripe restricted key is, and when it is the right answer",[10,292,293],{},"A key cut for one job instead of every job.",[10,295,296,297,300],{},"A restricted key begins ",[14,298,299],{},"rk_live_"," and carries only the permissions you tick\nwhen you create it. A key that may read invoices cannot issue a refund. A key\nthat may create charges cannot read your customer list. Stripe recommends\nmigrating from secret keys to restricted ones for exactly this reason, and it is\ngood advice about the code running on your server.",[10,302,303],{},"It is not a way to make a browser key acceptable. Two of the 30,998 apps in our\nsweep were shipping a restricted key in the frontend, and both came back graded\nC. Our scan treats a restricted key as a high finding where a secret key is a\ncritical one, and a single high finding caps the grade at C. Whoever finds that\nkey still gets every permission you ticked, from anywhere.",[10,305,306],{},"Where a restricted key earns its place is the awkward middle case, and vibe-coded\napps produce a lot of those. An automation tool that needs to read your payouts.\nA reporting script someone on Fiverr wrote for you. An Edge Function that only\never creates one kind of charge. Each of those runs on a server and each of them\nneeds a fraction of your account, so each of them gets its own key with that\nfraction ticked, and the day one leaks you revoke one key instead of re-wiring\nyour entire integration.",[43,308,310],{"id":309},"how-to-check-what-your-app-is-actually-shipping","How to check what your app is actually shipping",[10,312,313,314,316,317,319,320,322],{},"Start by hand, because it costs nothing and needs nothing installed. Open your\nlive site, view the page source, and search it for ",[14,315,16],{},", then ",[14,318,299],{},",\nthen ",[14,321,28],{},". Finding the third and not the first two is the result you want.",[10,324,325,326,328,329,331],{},"What a page-source search misses is the JavaScript the page loads afterwards,\nwhich on an app built by Lovable, Bolt or Replit is nearly all of it. Our free\nscanner opens your app in a real browser, waits for the bundles to arrive, and\nreads those. It classifies what it finds rather than matching key-shaped\nstrings, so ",[14,327,28],{}," comes back marked as correct and\n",[14,330,16],{}," comes back as a critical finding, and the two are never in the same\npile.",[214,333],{"alt":334,"caption":335,"src":336},"The Reeve mark above a grid of nine outlined icons, one per check: a key, a database cylinder, a document, a padlock, a lidded box, a pair of code brackets, a globe, a certificate rosette, and a calendar.","Keys are one of nine read-only checks. The rest are why a grade tells you more than a search of your own page source does.","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fnine-checks-1600x420.png",[10,338,339],{},"You get a grade, a score and the counts on screen in about 20 seconds, with no\naccount. Give an email address and the detailed list comes with it, along with a\nfix written for the builder you used that you can paste straight in.",[10,341,342,343,346,347,351,352,356],{},"Three things the scan will not do, which are the reasons it is safe to point at\na live app taking real payments: it never logs in, it never writes anything, and\nit never keeps a key it finds. An exposed secret is stored as a masked hint of\nthe shape ",[14,344,345],{},"sk_live_…a1b2",", and the real value is thrown away.\n",[37,348,350],{"href":349},"\u002F#scan","Scan your app",", or read\n",[37,353,355],{"href":354},"\u002Fsecurity-scanner","what each of the nine checks looks at"," first.",[43,358,360],{"id":359},"what-to-do-right-now","What to do right now",[362,363,364],"key-takeaways",{},[365,366,367,379,382,385,388,391],"ul",{},[243,368,369,370,372,373,375,376,378],{},"Read the second character before anything else. ",[14,371,28],{}," in your bundle is correct and needs no action; ",[14,374,16],{}," and ",[14,377,299],{}," do.",[243,380,381],{},"Create the replacement key first and confirm a real payment on it, then expire the old one at Stripe. Expiring is what closes the door.",[243,383,384],{},"Deleting the key from your code closes nothing on its own. The file that carried it is already downloaded, cached and in your version history.",[243,386,387],{},"Move whatever needed the key onto a server: an Edge Function, a serverless route, anything that is not the browser.",[243,389,390],{},"Read your payment history and your customer list for the period the key was live. Rotation stops what happens next and says nothing about what already happened.",[243,392,393],{},"Give each server-side job its own restricted key with only the permissions it needs, so the next leak costs you one key rather than all of them.",[10,395,396],{},"A Stripe key usually arrives late. The app ships, it runs for a while, and then\none day you add checkout, and that is the deploy that puts a payment credential\ninto the bundle for the first time. Whatever scan you ran at launch was a\nphotograph of an app that could not take money yet.",[10,398,399,403],{},[37,400,402],{"href":401},"\u002Fpricing","Reeve Monitor"," is built for that gap. It re-runs all nine checks\nevery hour on up to three apps, watches uptime every 60 seconds, tells you the\nday a result changes rather than waiting for you to look, and sends a monthly\nreport in plain language. A key that reaches the bundle with a Thursday deploy\nis in that hour's re-scan. It is $12 a month at list, with seven days free\nbefore it charges you, and the pricing page is sometimes below the figure here\nand never above it.",[10,405,406,407,411,412,416,417,421,422,41],{},"If you would rather work through this as a list, the\n",[37,408,410],{"href":409},"\u002Fchecklist","10-minute security checklist"," covers this alongside the other\nthings worth closing in a newly launched app. For the wider question of which\nkeys belong in a browser at all, we have\n",[37,413,415],{"href":414},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","a guide to telling publishable keys from secret ones",",\n",[37,418,420],{"href":419},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","the same question for an OpenAI key",",\nand ",[37,423,425],{"href":424},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","a census of what 30,998 apps actually shipped",{"title":427,"searchDepth":428,"depth":428,"links":429},"",3,[430,432,433,434,435,436,437],{"id":45,"depth":431,"text":46},2,{"id":73,"depth":431,"text":74},{"id":205,"depth":431,"text":206},{"id":234,"depth":431,"text":235},{"id":289,"depth":431,"text":290},{"id":309,"depth":431,"text":310},{"id":359,"depth":431,"text":360},"Security basics","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcover-1200x630.png","A shop till with its cash drawer standing open, and a key left hanging in the lock on the front of the machine.","A Stripe secret key exposed in your frontend can refund, charge and read every customer record you hold. Your pk_live_ key is meant to be there.",false,"md",[445,448,451,454,457,460],{"q":446,"a":447},"Is my Stripe publishable key safe in the frontend?","Yes. A key beginning pk_live_ is meant to be in the page, and Stripe says so in its own documentation: publishable keys are safe to expose in front-end code. It builds the payment form and tokenises a card. It cannot read your customers, move money or refund anything. If a scanner or a friend told you a Stripe key is exposed, read the second character before you do anything else, because a pk_live_ key sitting in your bundle is your integration working the way Stripe designed it.",{"q":449,"a":450},"What can someone do with a leaked Stripe secret key?","Everything you can do in your own dashboard that does not need your password. They can read your full customer list with names, email addresses, billing addresses and the last four digits of each card, read every payment you have ever taken, issue refunds until your balance is empty, create charges and payment links, and run stolen card numbers through your account to find the ones that still work. Stripe calls that last one card testing, and it arrives at your door as disputes and declines on an account you thought was quiet.",{"q":452,"a":453},"How do I roll a Stripe key without downtime?","Create the replacement first. In the Stripe Dashboard, create a new secret key, put it where the old one lived on your server, deploy, and confirm one real payment goes through on the new key. Only then expire the old one, which stops it making any further API calls. If the key is already public and you would rather lose a few payments than leave it live, roll it instead: rolling blocks the key immediately and generates a new one, and Stripe notes that webhook endpoints created with the old key stay active.",{"q":455,"a":456},"What is a Stripe restricted key?","A key you cut for one job. A restricted key begins rk_live_ and carries only the permissions you tick when you create it, so a key that may read invoices cannot issue a refund. Stripe recommends moving from secret keys to restricted ones for exactly that reason. Read it as advice about the code on your server, not as a way to make a browser-side key acceptable: a restricted key in your bundle is still a credential a stranger can use, and our scan grades one as a high finding.",{"q":458,"a":459},"Does Stripe tell me if my key leaks?","Sometimes, and you cannot plan on it. Stripe says it proactively scans the internet for leaked API keys using tools like the GitHub token scanner, and it may notify you or deactivate a key it finds. Its own best-practices page adds that detection is not guaranteed. The scanning that works best is aimed at public code repositories, and a key compiled into the JavaScript on your own domain is not a repository. Treat any key you have seen somewhere it should not be as compromised, whether or not Stripe has said anything.",{"q":461,"a":462},"Is a test key (sk_test_) dangerous in my frontend?","It is a much smaller problem than a live one and it is still worth closing. A test key touches only your sandbox, so nobody can take your money with it. What it does hand over is a working map of how your integration is put together, and it tends to mean the same copy-paste habit is one deploy away from shipping the live key instead. Stripe treats any secret or restricted key seen where it should not be as compromised. Expire it and move the call to a server.","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",[465,466,467,468,469,470,471],"stripe secret key exposed","sk_live key in frontend","stripe key leaked what to do","stripe restricted key vs secret key","is my stripe publishable key safe","roll stripe api key","stripe key in javascript bundle",{},true,"Stripe secret key exposed in your frontend?","\u002Fblog\u002Fstripe-secret-key-in-frontend","2026-09-19",{"title":5,"description":441},"blog\u002Fstripe-secret-key-in-frontend",[480,481,482,483],"A Stripe secret key exposed in your frontend is the leak that gets charged to your account: refunds, new charges, and every customer record you hold.","pk_live_ belongs in your app and always did. sk_live_ differs from it by one character and has unrestricted permissions across your whole Stripe account.","Create the replacement key first, put it live, then expire the old one. Deleting the line from your code recalls nothing anyone already downloaded.","We found a live Stripe secret key in 3 of 30,998 scanned apps. All three came out graded D.","WefQeAgenZI0T7IxU7aphRxoupNdEf-QZr_LMnkSLFY",[486,493,499,505,511,512,518,524,530,536,542,548,554,560,566,572,578,584,590,596,602,607,612,618,624,630,636,642,648,654,660,666,672,678,684,690,695,701,707,713,718,724],{"path":487,"title":488,"description":489,"published":490,"category":491,"image":492,"draft":442},"\u002Fblog\u002Fsupabase-backup-auth-users","Why your Supabase dump has no users in it","Run supabase db dump on its own and you get the shape of your database and none of its rows, with the auth schema your users live in left out entirely.","2026-09-23","Backups","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":494,"title":495,"description":496,"published":497,"category":438,"image":498,"draft":442},"\u002Fblog\u002Fdomain-and-certificate-expiry","Domain expired, website down: what actually happens next","Your domain expired and your website is down. Here is the clock you are on, why a lapsed certificate is the easier of the two, and how to check both.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":500,"title":501,"description":502,"published":503,"category":438,"image":504,"draft":442},"\u002Fblog\u002Fis-lovable-safe","Is Lovable safe? What 18,554 live Lovable apps showed","Is Lovable safe? We ran nine checks on 18,554 live Lovable apps. The platform was the cleanest of five builders. Every finding was inside the app itself.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":506,"title":507,"description":508,"published":509,"category":438,"image":510,"draft":442},"\u002Fblog\u002Fvibe-coded-app-security-checklist","The vibe coding security checklist, in nine checks","A vibe coding security checklist with nine items, each one something anyone can verify about your live app from outside, and each with a one-line test.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":475,"title":5,"description":441,"published":476,"category":438,"image":463,"draft":442},{"path":513,"title":514,"description":515,"published":516,"category":438,"image":517,"draft":442},"\u002Fblog\u002Fvite-and-next-public-env-vars","Vite env variables exposed: VITE_ and NEXT_PUBLIC_ mean publish this","Vite env variables exposed in your app did what the prefix asked. VITE_ and NEXT_PUBLIC_ mean publish this, and the AI that added one never knew the cost.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":519,"title":520,"description":521,"published":522,"category":438,"image":523,"draft":442},"\u002Fblog\u002Fbase44-source-maps","Base44 security: what a scan flags, and what is yours to fix","Base44 security on 5,438 scanned apps: three findings on nearly every one are the platform's, the source map is Base44's badge, and what is yours is short.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":525,"title":526,"description":527,"published":528,"category":438,"image":529,"draft":442},"\u002Fblog\u002Fis-cursor-ai-safe","Is Cursor AI safe? The editor, the code, and the app you shipped","Is Cursor AI safe? Three questions in one search: what Cursor keeps, what the code it writes gets wrong, and whether the app you shipped is open.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":531,"title":532,"description":533,"published":534,"category":438,"image":535,"draft":442},"\u002Fblog\u002Fis-replit-safe","Is Replit safe? What we found in 3,042 live Replit apps","Is Replit safe? We ran nine external checks on 3,042 live Replit apps. The host was not where the findings were. The app each owner published was.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":537,"title":538,"description":539,"published":540,"category":491,"image":541,"draft":442},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: why your database copy has no files","A Supabase storage backup is a separate job. Database backups keep the list of your files and none of the files, so a restore leaves every upload broken.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":543,"title":544,"description":545,"published":546,"category":491,"image":547,"draft":442},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Supabase point-in-time recovery: what it costs, what it misses","Supabase point-in-time recovery rewinds your database to any second in the last week. It costs $100 a month on top of Pro, and it covers your database only.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":549,"title":550,"description":551,"published":552,"category":491,"image":553,"draft":442},"\u002Fblog\u002Fsupabase-project-paused-recover","Supabase project paused? Your data is still there","Supabase paused your project after a week of inactivity. Nothing is deleted, Restore sits beside the project name, and you have a year before that changes.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":555,"title":556,"description":557,"published":558,"category":438,"image":559,"draft":442},"\u002Fblog\u002Fsafest-ai-app-builder","Which AI app builder is safest? We scanned 30,998 apps","Which AI app builder is safest? We scanned 30,998 live apps from Lovable, Base44, Replit, v0 and Bolt. The builder is not what decides your grade.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":561,"title":562,"description":563,"published":564,"category":438,"image":565,"draft":442},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","Enable Row Level Security on every Supabase table, then prove it","Enabling Row Level Security in Supabase with no policy locks a table completely. A policy without the setting does nothing. Here is the SQL, and the test.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":567,"title":568,"description":569,"published":570,"category":438,"image":571,"draft":442},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": what the warning misses","Supabase reports \"RLS disabled in public\" as an error. It says nothing about the read policy that leaves your table just as open to strangers.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":573,"title":574,"description":575,"published":576,"category":438,"image":577,"draft":442},"\u002Fblog\u002Frotate-supabase-service-role-key","How to rotate a leaked Supabase service_role key","Supabase says fix the leak first. Other guides say rotate now. Which is right depends on where your service_role key leaked.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":579,"title":580,"description":581,"published":582,"category":438,"image":583,"draft":442},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Vibe coding security scanners compared, including ours","The best vibe coding security scanner comes down to three questions no feature list answers. Ten tools compared, with prices, and the jobs Reeve does not do.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":585,"title":586,"description":587,"published":588,"category":438,"image":589,"draft":442},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: run the five checks yourself","A Supabase security checker reads your published app instead of your project settings. Here are the five checks it runs, and how to run each one yourself.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":591,"title":592,"description":593,"published":594,"category":438,"image":595,"draft":442},"\u002Fblog\u002Fvibe-coding-security-scanner","Vibe coding security scanner: what a URL scan misses","A vibe coding security scanner reads your live app from outside. Here is what that covers, the four things it cannot see, and how to read the result.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":597,"title":598,"description":599,"published":600,"category":438,"image":601,"draft":442},"\u002Fblog\u002Fmissing-security-headers","Missing security headers: when it actually matters","Missing security headers is the finding our scanner prints most. Here is what it protects against, and when it is the least urgent line on your report.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":419,"title":603,"description":604,"published":605,"category":438,"image":606,"draft":442},"Your OpenAI API key is exposed in your frontend. Rotate it.","An OpenAI API key exposed in your frontend cannot be locked to a domain. Rotate it today, move the call behind your own endpoint, and cap the spend.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":424,"title":608,"description":609,"published":610,"category":438,"image":611,"draft":442},"An API key exposed in your frontend: what 30,998 apps shipped","An API key exposed in your frontend is usually a Google Maps key. We scanned 30,998 live vibe-coded apps and counted which secrets actually leak.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":613,"title":614,"description":615,"published":616,"category":491,"image":617,"draft":442},"\u002Fblog\u002Fsupabase-backup-tools-compared","Supabase backup tools compared, including ours","Four kinds of Supabase backup tool, what each one actually copies, and the case where a free GitHub Action beats paying anyone, us included.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":619,"title":620,"description":621,"published":622,"category":438,"image":623,"draft":442},"\u002Fblog\u002Freplit-secrets-explained","How to use secrets in Replit, and what still gets published","How to use secrets in Replit: add one, read it back, and fix the two reasons it comes back undefined. Plus the keys the Secrets tool cannot keep private.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":625,"title":626,"description":627,"published":628,"category":491,"image":629,"draft":442},"\u002Fblog\u002Fback-up-supabase-free-tier","Supabase free tier backups: how to make one without a terminal","There are no Supabase free tier backups, so the copy has to come from you. How to make one from the dashboard, and what CSV leaves out.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":631,"title":632,"description":633,"published":634,"category":438,"image":635,"draft":442},"\u002Fblog\u002Fis-supabase-secure","Is Supabase secure? Yes. Your project is a separate question","Is Supabase secure? The platform is audited, encrypted and pen-tested. Their own compliance documents say where that stops and your settings begin.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":637,"title":638,"description":639,"published":640,"category":438,"image":641,"draft":442},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Where to find your Supabase API keys: anon, service_role and the URL","Your Supabase project URL, anon key and service_role key are on one dashboard page. Here is where that page is, and which of the four belongs in your app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":643,"title":644,"description":645,"published":646,"category":438,"image":647,"draft":442},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy in Supabase. Now what?","\"New row violates row-level security policy\" means Supabase refused a write. The fix that clears it in ten seconds also reopens the table to everyone.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":649,"title":650,"description":651,"published":652,"category":438,"image":653,"draft":442},"\u002Fblog\u002Fcors-wildcard-security-risk","Is a CORS wildcard a security risk? Usually not.","Is a CORS wildcard a security risk? Usually it is your builder default, and it gives away nothing your server was not already handing to anyone who asked.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":655,"title":656,"description":657,"published":658,"category":491,"image":659,"draft":442},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase branching is not a backup. It only goes forwards.","Supabase branching is not a backup: a branch starts with none of your data, and merging only moves schema. What it is for, and what to use instead.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":661,"title":662,"description":663,"published":664,"category":491,"image":665,"draft":442},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","How to restore a Supabase backup, and what breaks after","How to restore a Supabase backup from the dashboard or from a dump file, what the restore replaces, and why your app can still be broken when it finishes.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":667,"title":668,"description":669,"published":670,"category":438,"image":671,"draft":442},"\u002Fblog\u002Fsupabase-storage-bucket-public","Your Supabase storage bucket is public. Is that a problem?","A public Supabase storage bucket means anyone with a file URL can open it. It does not mean anyone can list what is in there. Two different settings.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":673,"title":674,"description":675,"published":676,"category":438,"image":677,"draft":442},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","Is a Google API key exposed in your frontend a problem?","A Google API key exposed in your frontend is the key our scanner finds most often, and usually it is fine. One free setting decides which it is.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":679,"title":680,"description":681,"published":682,"category":491,"image":683,"draft":442},"\u002Fblog\u002Fai-agent-deleted-my-database","An AI agent deleted my Supabase data. What can I recover?","An AI agent deleted your database data. What you can recover was decided before it ran, and the next few minutes decide how much of it survives.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":685,"title":686,"description":687,"published":688,"category":438,"image":689,"draft":442},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","Can anyone read your Supabase database? We checked 3,680 apps","Can anyone read your Supabase database without logging in? We scanned 30,998 live apps built with AI builders and measured how often the answer is yes.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":691,"title":692,"description":693,"published":688,"category":438,"image":694,"draft":442},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps exposed: your app is publishing its original code","An exposed source map lets anyone read your app's original code, comments included. The 30-second check, and what actually matters if yours are public.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":696,"title":697,"description":698,"published":699,"category":438,"image":700,"draft":442},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase's new API keys: which one is safe in your app?","Supabase replaced anon and service_role with publishable and secret keys. Which one belongs in your app, and which never does?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":702,"title":703,"description":704,"published":705,"category":491,"image":706,"draft":442},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","Does Supabase back up my database? It depends on your plan.","Does Supabase back up your database? Daily on paid plans, and not at all on the free one. How to check which you have, and what that copy cannot survive.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":708,"title":709,"description":710,"published":711,"category":438,"image":712,"draft":442},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security is on. Your table is still public.","Turning on Supabase Row Level Security does not protect a table. Your policies do, and the policy that fixed your broken app may let everyone in.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":714,"title":715,"description":716,"published":711,"category":491,"image":717,"draft":442},"\u002Fblog\u002Fversion-history-is-not-a-backup","Version history is not a backup. It cannot undo a deleted table.","Lovable and Bolt keep version history for your code. Your database is a separate service, so rolling back to this morning does not bring your data back.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":719,"title":720,"description":721,"published":722,"category":491,"image":723,"draft":442},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Three ways to back up a Supabase database, and what each misses","The dashboard, pg_dump, and a managed service. What each one actually saves, what it quietly leaves out, and which one survives losing the account.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":414,"title":725,"description":726,"published":722,"category":438,"image":727,"draft":442},"Which API keys are safe in your frontend, and which aren't","Your Supabase anon key is supposed to be public. Your service_role key is not, and it ignores every rule you set. Here is how to tell them apart.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1790150951364]