[{"data":1,"prerenderedAt":663},["ShallowReactive",2],{"blog-en-what-secrets-leak-from-vibe-coded-apps":3,"blog-index-en":422},{"id":4,"title":5,"body":6,"category":381,"cover":382,"coverAlt":383,"description":384,"draft":385,"extension":386,"faq":387,"image":403,"keywords":404,"meta":410,"navigation":411,"ogTitle":412,"path":413,"published":414,"seo":415,"stem":416,"tldr":417,"updated":414,"__hash__":421},"blog_en\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps.md","An API key exposed in your frontend: what 30,998 apps shipped",{"type":7,"value":8,"toc":369},"minimark",[9,13,16,21,24,33,45,49,52,59,69,75,86,90,93,180,183,189,200,208,212,218,237,242,248,252,255,269,277,281,299,308,311,327,331,357],[10,11,12],"p",{},"Someone opens your app, presses F12, and tells you there is an API key exposed\nin your frontend. The word they use is usually \"leaked\". They rarely say which\nkey, and the advice you find afterwards treats every one of them as the same\nemergency.",[10,14,15],{},"They are not the same emergency, and we can now put numbers on the gap. Between\n12 and 14 August 2026 we ran nine external checks over 30,998 live apps built\nwith Lovable, Bolt, v0, Replit and Base44, and read the JavaScript each one\nhands to a browser. This is what was in it.",[17,18,20],"h2",{"id":19},"is-an-api-key-exposed-in-your-frontend-actually-a-problem","Is an API key exposed in your frontend actually a problem?",[10,22,23],{},"Usually not, and the shape of \"usually\" is more lopsided than we expected.",[10,25,26,27,32],{},"We found a key worth naming in 1,332 of the 30,998 apps. In 1,080 of those, the\nonly thing we found was a Google API key, which is the credential that is\n",[28,29,31],"a",{"href":30},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","supposed to be in your page",". What\nprotects that one is a setting held on Google's side, and hiding it was never\npart of the arrangement.",[10,34,35,36,40,41,44],{},"The other 29,666 apps shipped nothing our secrets check treats as a problem.\nThat figure needs one qualification to be honest. Publishable keys are excluded\nfrom it: a Supabase ",[37,38,39],"code",{},"anon"," key or a Stripe ",[37,42,43],{},"pk_"," key belongs in the browser, so\nwe mark it as something you got right and it never enters these counts.",[17,46,48],{"id":47},"what-we-counted-and-what-we-could-not","What we counted, and what we could not",[10,50,51],{},"We loaded each app the way a visitor does, in a real browser, and read the\nJavaScript it downloaded. Everything below is a shape found in that code.",[10,53,54,58],{},[55,56,57],"strong",{},"We match key formats we know."," Stripe, OpenAI, Anthropic, AWS, Google and\nSupabase have recognisable prefixes, and a Supabase JWT states its own role in\nreadable text in the middle section. A credential in a format we do not\nrecognise is not in these numbers, so read them as a floor.",[10,60,61,64,65,68],{},[55,62,63],{},"We never used a key we found."," Not once, on any app. We recorded the type\nand a masked hint of the form ",[37,66,67],{},"sk_live_…a1b2",", and the real value was never\nwritten down anywhere.",[10,70,71,74],{},[55,72,73],{},"No app is named."," Not here, not in the dataset, not anywhere we publish.",[10,76,77,80,81,85],{},[55,78,79],{},"Nearly all of these are apps published on a builder's own domain."," The full\nmethod, the sample and every figure behind this article are in\n",[28,82,84],{"href":83},"\u002Fresearch\u002Fvibe-coded-app-security-2026","our scan report",", including the parts\nwe could not measure.",[17,87,89],{"id":88},"what-the-30998-apps-were-actually-shipping","What the 30,998 apps were actually shipping",[10,91,92],{},"One table, ordered by how often we saw each thing. The secrets check completed\non every one of the 30,998 apps, so every count below is out of all of them.",[94,95,96,109],"table",{},[97,98,99],"thead",{},[100,101,102,106],"tr",{},[103,104,105],"th",{},"What we found in the browser code",[103,107,108],{},"Apps",[110,111,112,121,129,137,145,153,165,172],"tbody",{},[100,113,114,118],{},[115,116,117],"td",{},"Google API key",[115,119,120],{},"1,142",[100,122,123,126],{},[115,124,125],{},"A high-entropy value beside a \"secret\" or \"password\" name",[115,127,128],{},"204",[100,130,131,134],{},[115,132,133],{},"OpenAI key",[115,135,136],{},"33",[100,138,139,142],{},[115,140,141],{},"AWS access key",[115,143,144],{},"9",[100,146,147,150],{},[115,148,149],{},"Anthropic key",[115,151,152],{},"5",[100,154,155,162],{},[115,156,157,158,161],{},"Supabase ",[37,159,160],{},"service_role"," key",[115,163,164],{},"3",[100,166,167,170],{},[115,168,169],{},"Stripe secret key",[115,171,164],{},[100,173,174,177],{},[115,175,176],{},"Stripe restricted key",[115,178,179],{},"2",[10,181,182],{},"The rows add up to more than 1,332 because one app can carry two of these. Sort\nthe same 1,332 apps into buckets that do not overlap and the picture gets\nstarker: 1,080 had a Google key and nothing else, 198 had a high-entropy value\nthat may or may not be a real credential, and 54 had a key that is a genuine\nsecret by its own shape.",[184,185],"diagram",{"alt":186,"caption":187,"src":188},"A single wide bar representing the 1,332 apps where a key was found, divided into three parts: a long teal section of 1,080, a shorter amber section of 198, and a narrow red section of 54 at the right-hand end.","Every app where we found a key at all, sorted into three groups that do not overlap. The red section is the one the warnings are about.","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fwhat-we-found-1600x620.png",[10,190,191,192,195,196,199],{},"That second bucket is worth naming carefully. A high-entropy value sitting next\nto a word like ",[37,193,194],{},"secret"," or ",[37,197,198],{},"password"," might be a live credential, or it might be\na session identifier, a build hash or a public token with an unlucky name. We\nflag it as worth a look, and from outside nobody can tell you which it is.",[10,201,202,203,207],{},"The 54 in the third bucket are the real thing. All but two of them came out\ngraded D or F, because a single critical finding caps the grade at D no matter\nwhat else the app got right. The largest single group inside it is the OpenAI\nkey, at 33, and that one has\n",[28,204,206],{"href":205},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","no setting that would make it safe in a browser",".",[17,209,211],{"id":210},"the-key-everyone-warns-about-was-the-rarest-thing-we-found","The key everyone warns about was the rarest thing we found",[10,213,214,215,217],{},"Three apps. That is how many shipped a Supabase ",[37,216,160],{}," key, the one that\nwalks past every table rule you have written. A Stripe secret key also appeared\nthree times.",[10,219,220,221,223,224,227,228,227,231,227,234,207],{},"Set that against the other half of the same sweep. Of the apps we scanned, 8,429\nnamed a Supabase project, and both findings sit inside that one group. Three of\nthem shipped the ",[37,222,160],{}," key. In 2,096 of them, at least one table\nanswered a request carrying no login at all, and in 394 that table was named\nafter people: ",[37,225,226],{},"users",", ",[37,229,230],{},"profiles",[37,232,233],{},"customers",[37,235,236],{},"orders",[184,238],{"alt":239,"caption":240,"src":241},"Two rows drawn against the same scale. The upper row, marked with a key, is a red sliver so thin it needs a dashed ring around it to be visible, labelled 3. The lower row, marked with a table, is a teal band running most of the width, labelled 2,096.","Both counts come from the same 8,429 Supabase apps. The upper row is the finding owners are warned about.","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fwarned-about-vs-found-1600x680.png",[10,243,244,245,247],{},"The 2,096 is a floor. 4,749 of the 8,429 never answered our database check, for\nreasons we cannot see from outside, and those are recorded as unknown rather\nthan as clean. The three ",[37,246,160],{}," keys are an exact count, because a key\nis read out of code that every app hands over.",[17,249,251],{"id":250},"why-the-warnings-point-at-the-wrong-key","Why the warnings point at the wrong key",[10,253,254],{},"We only see the outside of these apps, so we cannot tell you why. What we can\nshow is which mistake survives.",[10,256,257,258,260,261,263,264,268],{},"Copying the wrong Supabase key is genuinely easy to do. In an older project\n",[37,259,39],{}," and ",[37,262,160],{}," sit next to each other in the same dashboard panel,\nthey are the same length and the same shape, and\n",[28,265,267],{"href":266},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","nothing breaks if you take the wrong one",".\nIt still only happened three times in 30,998 apps, because nothing pushes you\ntowards it. Pasting either key makes the app work.",[10,270,271,272,276],{},"The open table has a force behind it. Row Level Security gets switched on, the\napp stops showing data, a policy that allows everybody gets written to make it\nwork again, and from that moment on the dashboard reports the table as\nprotected. Switching it on\n",[28,273,275],{"href":274},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","is not the same as being protected",".\nOur scan grades that table a critical finding on a day your dashboard is showing\nyou the switch turned on and a policy in place.",[17,278,280],{"id":279},"how-to-find-an-exposed-api-key-in-your-app-for-free","How to find an exposed API key in your app, for free",[10,282,283,284,287,288,291,292,294,295,298],{},"Start by hand, because it costs five minutes and needs nothing installed. Open\nyour live site, view the page source, and search it for four strings: ",[37,285,286],{},"AIza"," for\na Google key, ",[37,289,290],{},"sk_"," for a Stripe or model-provider secret, ",[37,293,160],{}," for\nthe Supabase key that ignores your rules, and ",[37,296,297],{},"eyJ"," for any Supabase token.\nAnything that comes back is already in the hands of every visitor you have.",[10,300,301,302,304,305,307],{},"That gets you the page itself. What it misses is the JavaScript the page loads\nafterwards, which is why our own scanner opens an app in a real browser and\nreads the bundles instead of the HTML. Searching by hand also cannot tell you\nwhether the ",[37,303,297],{}," token you found is the ",[37,306,39],{}," key or the secret one, because\nthe two are the same length and the same shape.",[10,309,310],{},"Our free scan covers both. It loads your app in a real browser, reads the code\nthat actually arrives, decodes each Supabase token and reports the role written\ninside it, so a publishable key comes back marked correct instead of buried in a\nwall of red. You get a grade, a score and the counts on screen in about 20\nseconds with no account. Give an email address and you get the detailed list\ntoo, with a fix written for your builder that you can paste straight in.",[10,312,313,314,316,317,321,322,326],{},"Three things it will not do, which are the reasons it is safe to run on a live\napp: it never logs in, it never writes anything, and it never keeps a key it\nfinds. An exposed secret is stored as a masked hint like ",[37,315,67],{},", and\nthe real value is discarded. ",[28,318,320],{"href":319},"\u002F#scan","Scan your app",", or read\n",[28,323,325],{"href":324},"\u002Fsecurity-scanner","what each of the nine checks looks at"," first.",[17,328,330],{"id":329},"what-to-do-in-the-order-the-numbers-suggest","What to do, in the order the numbers suggest",[332,333,334],"key-takeaways",{},[335,336,337,345,348,351,354],"ul",{},[338,339,340,341,344],"li",{},"Identify the key before you react to it. The prefix answers it for Stripe and for newer Supabase keys; for an older Supabase key, the ",[37,342,343],{},"role"," field inside the token decides.",[338,346,347],{},"If it is a Google key, restrict it rather than hiding it. Websites restriction, your domain, only the APIs you use, plus a daily quota cap. Free, about five minutes, no code change.",[338,349,350],{},"If it is a genuine secret key, rotate it first. Deleting it from your code closes nothing, because the old value is still in your version history and in cached copies of your site.",[338,352,353],{},"Then go and read your table rules, which is where the numbers say the real exposure is. Start with the tables holding people.",[338,355,356],{},"Check billing and logs after any secret-key exposure. Rotation stops what happens next, and says nothing about what already happened.",[10,358,359,360,364,365,207],{},"Work through the whole list in one sitting with the\n",[28,361,363],{"href":362},"\u002Fchecklist","10-minute security checklist",", which covers the key and the table\nrules alongside the other things worth closing in a newly launched app. And if\nthe database half of this article is the part that worries you, it has a census\nof its own:\n",[28,366,368],{"href":367},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","can anyone read your Supabase database",{"title":370,"searchDepth":371,"depth":371,"links":372},"",3,[373,375,376,377,378,379,380],{"id":19,"depth":374,"text":20},2,{"id":47,"depth":374,"text":48},{"id":88,"depth":374,"text":89},{"id":210,"depth":374,"text":211},{"id":250,"depth":374,"text":251},{"id":279,"depth":374,"text":280},{"id":329,"depth":374,"text":330},"Security basics","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcover-1200x630.png","A page of app code with eight key-shaped values highlighted in it, most of them ordinary and one drawn in red.","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.",false,"md",[388,391,394,397,400],{"q":389,"a":390},"How do I know if my API key is leaked?","Open your live site, view the page source, and search it for the shapes: AIza for a Google key, sk_ for a Stripe or model-provider secret, and eyJ for a Supabase JWT. Anything that turns up is in the hands of every visitor already. Our free scan does the same read from outside and reports what it can see in about 20 seconds, with no account needed for the grade.",{"q":392,"a":393},"Is a hardcoded API key always a security problem?","No, and treating it that way is how people learn to ignore the warning. Some keys are published on purpose: a Supabase anon key, a Stripe pk_ key and a Firebase web config key are all designed to sit in the browser, and what protects your data is the rules behind them. A secret key is the opposite case and belongs on a server. The prefix tells you which one you are looking at.",{"q":395,"a":396},"Someone said my Supabase key is exposed. Is that the bad one?","Almost certainly not. Both Supabase keys look alike, so read the role inside the token or check the prefix: anon and sb_publishable_ are meant to be public, service_role and sb_secret_ are not. We found a service_role key in 3 apps out of 30,998, so the odds are heavily on the harmless one. If it is the secret one, rotate it in the Supabase dashboard today.",{"q":398,"a":399},"What should I actually worry about instead?","The table rules on your database. Of the apps where we could complete that check, more than half had at least one table that answered a request carrying no login at all, and in 394 of them the open table was named after people: users, profiles, customers, orders. That is far commoner than any leaked key and it is much quieter, because the app works exactly the same either way.",{"q":401,"a":402},"I removed the key from my code. Is it closed now?","Not on its own. The old value still exists in your version history and in any cached copy of the page, so anyone who already collected it can keep using it. Rotating the key in the provider dashboard is what actually closes the door, and it is the first step rather than the last. After that, check billing and logs for usage you cannot account for.","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",[405,406,407,408,409],"api key exposed in frontend","secrets in client side code","hardcoded api key","is my api key leaked","find exposed api keys",{},true,"An API key exposed in your frontend: 30,998 apps counted","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","2026-09-02",{"title":5,"description":384},"blog\u002Fwhat-secrets-leak-from-vibe-coded-apps",[418,419,420],"An API key exposed in your frontend is nearly always the harmless kind. We found a key worth naming in 1,332 of 30,998 apps, and 1,080 of those carried a Google API key and nothing else.","The Supabase service_role key, the one every tutorial warns about, appeared in 3 apps out of 30,998. A Stripe secret key also appeared in 3.","Keys that spend money or read data by their own shape appeared in 54 apps. An open Supabase table appeared in 2,096.","UwgeoWdatW-ODcFSi0PSic6bdiUfepsajSilqFIcIOM",[423,430,436,442,448,454,460,466,472,478,484,490,496,502,508,514,520,526,532,538,544,549,550,556,562,568,574,580,586,592,598,604,610,615,621,626,631,637,643,648,653,659],{"path":424,"title":425,"description":426,"published":427,"category":428,"image":429,"draft":385},"\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":431,"title":432,"description":433,"published":434,"category":381,"image":435,"draft":385},"\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":437,"title":438,"description":439,"published":440,"category":381,"image":441,"draft":385},"\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":443,"title":444,"description":445,"published":446,"category":381,"image":447,"draft":385},"\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":449,"title":450,"description":451,"published":452,"category":381,"image":453,"draft":385},"\u002Fblog\u002Fstripe-secret-key-in-frontend","A Stripe secret key exposed in your frontend can move money","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.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":455,"title":456,"description":457,"published":458,"category":381,"image":459,"draft":385},"\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":461,"title":462,"description":463,"published":464,"category":381,"image":465,"draft":385},"\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":467,"title":468,"description":469,"published":470,"category":381,"image":471,"draft":385},"\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":473,"title":474,"description":475,"published":476,"category":381,"image":477,"draft":385},"\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":479,"title":480,"description":481,"published":482,"category":428,"image":483,"draft":385},"\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":485,"title":486,"description":487,"published":488,"category":428,"image":489,"draft":385},"\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":491,"title":492,"description":493,"published":494,"category":428,"image":495,"draft":385},"\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":497,"title":498,"description":499,"published":500,"category":381,"image":501,"draft":385},"\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":503,"title":504,"description":505,"published":506,"category":381,"image":507,"draft":385},"\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":509,"title":510,"description":511,"published":512,"category":381,"image":513,"draft":385},"\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":515,"title":516,"description":517,"published":518,"category":381,"image":519,"draft":385},"\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":521,"title":522,"description":523,"published":524,"category":381,"image":525,"draft":385},"\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":527,"title":528,"description":529,"published":530,"category":381,"image":531,"draft":385},"\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":533,"title":534,"description":535,"published":536,"category":381,"image":537,"draft":385},"\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":539,"title":540,"description":541,"published":542,"category":381,"image":543,"draft":385},"\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":205,"title":545,"description":546,"published":547,"category":381,"image":548,"draft":385},"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":413,"title":5,"description":384,"published":414,"category":381,"image":403,"draft":385},{"path":551,"title":552,"description":553,"published":554,"category":428,"image":555,"draft":385},"\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":557,"title":558,"description":559,"published":560,"category":381,"image":561,"draft":385},"\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":563,"title":564,"description":565,"published":566,"category":428,"image":567,"draft":385},"\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":569,"title":570,"description":571,"published":572,"category":381,"image":573,"draft":385},"\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":575,"title":576,"description":577,"published":578,"category":381,"image":579,"draft":385},"\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":581,"title":582,"description":583,"published":584,"category":381,"image":585,"draft":385},"\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":587,"title":588,"description":589,"published":590,"category":381,"image":591,"draft":385},"\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":593,"title":594,"description":595,"published":596,"category":428,"image":597,"draft":385},"\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":599,"title":600,"description":601,"published":602,"category":428,"image":603,"draft":385},"\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":605,"title":606,"description":607,"published":608,"category":381,"image":609,"draft":385},"\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":30,"title":611,"description":612,"published":613,"category":381,"image":614,"draft":385},"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":616,"title":617,"description":618,"published":619,"category":428,"image":620,"draft":385},"\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":367,"title":622,"description":623,"published":624,"category":381,"image":625,"draft":385},"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":627,"title":628,"description":629,"published":624,"category":381,"image":630,"draft":385},"\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":632,"title":633,"description":634,"published":635,"category":381,"image":636,"draft":385},"\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":638,"title":639,"description":640,"published":641,"category":428,"image":642,"draft":385},"\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":274,"title":644,"description":645,"published":646,"category":381,"image":647,"draft":385},"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":649,"title":650,"description":651,"published":646,"category":428,"image":652,"draft":385},"\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":654,"title":655,"description":656,"published":657,"category":428,"image":658,"draft":385},"\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":266,"title":660,"description":661,"published":657,"category":381,"image":662,"draft":385},"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]