[{"data":1,"prerenderedAt":998},["ShallowReactive",2],{"blog-en-api-key-leaked-what-to-do":3,"blog-index-en":645},{"id":4,"title":5,"body":6,"category":601,"cover":602,"coverAlt":603,"description":604,"draft":605,"extension":606,"faq":607,"image":622,"keywords":623,"meta":632,"navigation":633,"ogTitle":634,"path":635,"published":636,"seo":637,"stem":638,"tldr":639,"updated":636,"__hash__":644},"blog_en\u002Fblog\u002Fapi-key-leaked-what-to-do.md","Your API key leaked. Here is the order to do things in",{"type":7,"value":8,"toc":587},"minimark",[9,13,16,21,24,32,35,57,65,69,72,79,82,88,94,100,104,267,270,280,302,306,309,363,368,372,375,382,385,388,392,395,404,409,414,424,428,431,434,437,440,445,449,455,463,474,483,501,510,527,538,542,579],[10,11,12],"p",{},"The message usually comes from a scan report, from GitHub telling you a secret\nturned up in one of your repositories, or from somebody who pressed F12 on your\nsite and sent you a screenshot. However it reached you, you now know an API key\nof yours has leaked and you do not know what to do first.",[10,14,15],{},"Here is the part guide after guide gets wrong: they give you one answer for\nevery key, and the answer is always rotate. Some of those keys are supposed to\nbe public and need nothing done to them. Among the rest, some are quietly\nrunning up a bill while you read this, and some did all their damage on the day\nthey shipped. Those are three different situations, and the first move is\ndifferent in each.",[17,18,20],"h2",{"id":19},"first-is-the-key-actually-a-secret","First, is the key actually a secret?",[10,22,23],{},"Often it is not, and the numbers are lopsided enough to say so plainly.",[10,25,26,27,31],{},"We scanned 31,056 live apps and read the JavaScript each one ships to the\nbrowser. The check that looks for credentials answered on all of them, and\n1,332 came back with at least one key worth flagging. ",[28,29,30],"strong",{},"1,142 of those were\nGoogle API keys",", which is the one type among them that is usually sitting\nexactly where it belongs.",[10,33,34],{},"A Google API key in a web page is how Google Maps works. The key says which\nproject to bill, and what keeps a stranger from billing you with it is the\nrestriction on the key rather than the secrecy of it. Google's own guidance is\nblunt about both halves: \"Unrestricted API keys are insecure\", and \"You are\nfinancially responsible for charges caused by abuse of unrestricted API keys.\"\nThe fix for a key like that is to open it in the Cloud console and add two\nrestrictions, one naming your website and one naming the APIs you actually call.\nThe key string never changes.",[10,36,37,38,42,43,46,47,50,51,56],{},"The other family that belongs in the browser is the publishable keys. A Stripe\n",[39,40,41],"code",{},"pk_live_"," key, and a Supabase ",[39,44,45],{},"sb_publishable_"," or ",[39,48,49],{},"anon"," key, are built to be\nread by every visitor you have.\n",[52,53,55],"a",{"href":54},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Which of your keys are safe in your frontend","\nis the four-character version of that test.",[10,58,59,60,64],{},"If you would rather not go through your bundle by hand, our free scan reads your\nlive site, lists the keys it can see from outside, and says which kind each one\nis: ",[52,61,63],{"href":62},"\u002Fsecurity-scanner","scan your app",". It takes about 20 seconds and needs no\naccount.",[17,66,68],{"id":67},"my-api-key-leaked-what-do-i-do-first","My API key leaked. What do I do first?",[10,70,71],{},"Work out whether the key has a meter on it.",[10,73,74,75,78],{},"A metered key bills you per request. Google's, OpenAI's, Anthropic's, and an AWS\nkey with the wrong permissions behind it, are all meters: somebody else's\ntraffic lands on your invoice, at a rate they choose and you cannot see. An\nunmetered key reads or writes your data instead. A Supabase ",[39,76,77],{},"service_role"," key\nis the clearest example, and nothing about it costs money by the hour.",[10,80,81],{},"That single question sets the order.",[10,83,84,87],{},[28,85,86],{},"If the key has a meter, stop it now."," The feature that used it breaks until\nyou deploy the replacement, and that is the right trade, because the invoice is\nthe one part of this that keeps growing while you plan.",[10,89,90,93],{},[28,91,92],{},"If the key reads data and was published in your bundle, the read already\nhappened."," Every visitor who loaded the page has a copy, and so does every\ncrawler that went looking for that string. Nothing gets worse while you work out\nthe right order, and the thing worth getting right is not locking yourself out\nof your own app on the way.",[95,96],"diagram",{"alt":97,"caption":98,"src":99},"Two lanes, each starting with the same key. In the top lane the key runs to a dial whose needle has travelled well round, and on to a stack of coins, all in amber. In the bottom lane the key runs to a table whose rows are all lit in red, and the dial position is an empty dashed circle.","The top key is still costing you money while you read this. The bottom one did everything it was going to do on the day it shipped.","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fdoes-it-have-a-meter-1600x660.png",[17,101,103],{"id":102},"what-each-kind-of-key-can-actually-do","What each kind of key can actually do",[105,106,107,129],"table",{},[108,109,110],"thead",{},[111,112,113,117,120,123,126],"tr",{},[114,115,116],"th",{},"Key",[114,118,119],{},"Spends money",[114,121,122],{},"Reads your data",[114,124,125],{},"Can you restrict it instead?",[114,127,128],{},"Does replacing it break your app?",[130,131,132,154,169,188,205,227,248],"tbody",{},[111,133,134,142,145,148,151],{},[135,136,137,138,46,140],"td",{},"Supabase ",[39,139,45],{},[39,141,49],{},[135,143,144],{},"No",[135,146,147],{},"Only the rows your rules allow",[135,149,150],{},"Belongs in the browser",[135,152,153],{},"Not applicable",[111,155,156,161,163,165,167],{},[135,157,158,159],{},"Stripe ",[39,160,41],{},[135,162,144],{},[135,164,144],{},[135,166,150],{},[135,168,153],{},[111,170,171,177,180,182,185],{},[135,172,173,174],{},"Google ",[39,175,176],{},"AIza…",[135,178,179],{},"Yes, on your Cloud bill",[135,181,144],{},[135,183,184],{},"Yes, and Google says to try that first",[135,186,187],{},"Restricting it does not. Rotating it can.",[111,189,190,193,196,199,202],{},[135,191,192],{},"OpenAI or Anthropic key",[135,194,195],{},"Yes, at a rate a stranger sets",[135,197,198],{},"Files and assistants in the project",[135,200,201],{},"No publishable variant exists",[135,203,204],{},"Yes, until a server of yours holds it",[111,206,207,215,218,221,224],{},[135,208,158,209,46,212],{},[39,210,211],{},"sk_live_",[39,213,214],{},"rk_live_",[135,216,217],{},"Yes",[135,219,220],{},"Yes, customer and payment records",[135,222,223],{},"A restricted key is the narrow version",[135,225,226],{},"No, there is a seven-day window",[111,228,229,236,239,242,245],{},[135,230,231,232,235],{},"AWS ",[39,233,234],{},"AKIA…"," plus its secret half",[135,237,238],{},"Yes, including compute by the hour",[135,240,241],{},"Your S3 buckets",[135,243,244],{},"Deactivate it, which is reversible",[135,246,247],{},"No, you can hold two keys at once",[111,249,250,257,259,262,264],{},[135,251,137,252,46,255],{},[39,253,254],{},"sb_secret_",[39,256,77],{},[135,258,144],{},[135,260,261],{},"Every row in every table",[135,263,144],{},[135,265,266],{},"Yes, on an older project",[10,268,269],{},"Two notes on that table, because both change what you do next.",[10,271,272,273,276,277,279],{},"An AWS access key is two strings, an identifier beginning ",[39,274,275],{},"AKIA"," and a secret\nhalf, and AWS requires both together to sign a request. So an ",[39,278,275],{}," string on\nits own in a bundle cannot be used, and the reason to treat it as urgent anyway\nis that the two halves are nearly always pasted in together. Open the file and\nlook for the second one before you decide which case you are in.",[10,281,282,283,287,288,287,292,296,297,301],{},"The per-provider detail lives with each provider:\n",[52,284,286],{"href":285},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","an OpenAI key",",\n",[52,289,291],{"href":290},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","a Google API key",[52,293,295],{"href":294},"\u002Fblog\u002Fstripe-secret-key-in-frontend","a Stripe secret key",", and\n",[52,298,300],{"href":299},"\u002Fblog\u002Frotate-supabase-service-role-key","a Supabase service_role key",", which is\nthe one with a procedure of its own.",[17,303,305],{"id":304},"stopping-the-key-and-replacing-the-key-are-two-different-buttons","Stopping the key and replacing the key are two different buttons",[10,307,308],{},"Every provider in that table gives you both, and a panic reaches for the second\none.",[310,311,312,319,338,347],"ul",{},[313,314,315,318],"li",{},[28,316,317],{},"Google."," Restricting a key does not change the key string, so your app\ncarries on working. Their security guidance puts this before everything else:\n\"First try to restrict your API keys\", and rotation is the third option down,\nfor when a restriction is not possible.",[313,320,321,324,325,328,329,333,334,337],{},[28,322,323],{},"Stripe."," ",[28,326,327],{},"Expire key"," stops a key on its own, with no replacement\ninvolved. Their position on when to use it has no hedge in it: \"If a\nrestricted or secret API key is exposed or compromised, rotate it immediately\neven if you aren't sure anyone saw it.\" They also separate the two words\neverybody uses interchangeably. ",[330,331,332],"em",{},"Exposure"," is the key becoming visible\nsomewhere it should not have been. ",[330,335,336],{},"Compromise"," is evidence that somebody used\nit.",[313,339,340,324,343,346],{},[28,341,342],{},"AWS.",[28,344,345],{},"Deactivate"," is the control, and the useful part is that it can be\nundone. AWS says not to delete the old key at all while you are still\nchecking: \"we recommend that you do not immediately delete the first access\nkey. Instead, choose Actions and then choose Deactivate.\" If something you\nforgot turns out to need it, you switch it back on.",[313,348,349,352,353,355,356,358,359,362],{},[28,350,351],{},"Supabase, on an older project."," There is one switch, and it covers both\nlegacy keys. ",[39,354,49],{}," and ",[39,357,77],{}," are signed by the same secret, so\ndisabling the one you are trying to kill also stops the one your frontend is\nusing.\n",[52,360,361],{"href":299},"The four steps that avoid that"," are\nworth reading before you touch the switch rather than after.",[95,364],{"alt":365,"caption":366,"src":367},"A control panel with two switches. The left switch is thrown down and drawn in amber, with a crossed-out key beside it. The right switch is up and drawn in teal, with a key beside it and an arrow running from it to a working app window.","Two controls, and only the left one closes the hole. Which you reach for first is the decision this article is about.","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Ftwo-switches-1600x600.png",[17,369,371],{"id":370},"reading-the-log-for-the-window-the-key-was-out","Reading the log for the window the key was out",[10,373,374],{},"The window opens with the deploy that first shipped the key and closes when you\nstopped it. That date range is what you filter the provider's log to.",[10,376,377,378,381],{},"Every provider in the table keeps one. Stripe shows request logs for a single\nkey, from the overflow menu beside that key on the API keys page. AWS puts a\nlast-used date on each access key in the IAM console with no setup at all, and\nrecords the calls themselves in CloudTrail. Google charts usage per key in the\nCloud console, which is also the reading its own guidance tells you to do before\nyou change anything about a key. Supabase keeps API and database logs for the\nproject, and\n",[52,379,380],{"href":299},"narrowing the window on a Supabase key","\ncovers what to look for in them.",[10,383,384],{},"What you are looking for is traffic you cannot account for: requests to tables\nor endpoints your app never touches, volume at hours when nobody was using it,\ndeletes nobody made. Then look at the data itself, because a support message\nabout a record that changed on its own is how most of these are actually\ndiscovered, and it arrives weeks later.",[10,386,387],{},"You often cannot be certain, and Stripe says as much about its own detection:\n\"Stripe doesn't guarantee detection of all exposed or compromised keys.\" So\n\"nothing in the log\" is a result, and it is worth writing down with the date\nrange beside it.",[17,389,391],{"id":390},"rotating-without-taking-your-app-down","Rotating without taking your app down",[10,393,394],{},"Three of the four providers above hand you a window where the old key and the\nnew one both work, which is what stops this being an outage.",[10,396,397,399,400,403],{},[28,398,323],{}," \"When you rotate a key in the Dashboard, both the old and new keys\nwork for up to 7 days.\" The dialog also has a ",[28,401,402],{},"Now"," option, and their docs are\nexplicit that if you choose it the old key is deleted, which is the button for\nthe metered emergency rather than for a tidy migration. Their advice on when to\nlet the old one go is a measurement instead of a date: check its request logs,\nand expire it only after its request volume has been at zero for a few hours or\ndays.",[10,405,406,408],{},[28,407,317],{}," Rotating creates the new key carrying all of the old key's\nrestrictions, and, in their words, \"both the old and new key are accepted\"\nduring the window while you move your apps across. If you delete the old key too\nearly and something breaks, there is a way back: a deleted Google API key can be\nundeleted within 30 days.",[10,410,411,413],{},[28,412,342],{}," The sequence is in their documentation and it starts with the new key:\ncreate the second access key while the first is still active, move every\napplication onto it, check the last-used date on the old one, deactivate, and\nonly then delete. One ceiling to plan around is that an IAM user can hold a\nmaximum of two access keys, so a third application still on an old key has\nnowhere to go.",[10,415,416,418,419,355,421,423],{},[28,417,351],{}," No window. Direct rotation of the legacy\n",[39,420,49],{},[39,422,77],{}," keys is no longer supported, so invalidating one is a\nmigration onto the new key pair, and the order of the four steps is what keeps\nthe app up.",[17,425,427],{"id":426},"it-is-still-in-your-git-history","It is still in your git history",[10,429,430],{},"It is, and GitHub's documentation on that opens by telling you to do something\nelse first.",[10,432,433],{},"Their page on removing sensitive data sends you back to the key: \"if the\nsensitive data you need to remove is a secret (e.g.\npassword\u002Ftoken\u002Fcredential), as is often the case, then as a first step you need\nto revoke and\u002For rotate that secret\", and then, \"Once the secret is revoked or\nrotated, it can no longer be used for access, and that may be sufficient to\nsolve your problem. Going through the extra steps to rewrite the history and\nremove the secret may not be warranted.\"",[10,435,436],{},"The reason they say that is what a force push does not reach. After you rewrite\nyour history, the old commits are still there \"In any clones or forks of your\nrepository\" and \"Directly via their SHA-1 hashes in cached views on GitHub\".\nSupport can clear the cached views and the pull request references if you ask,\nand they draw their own line: they \"will only assist in the removal of sensitive\ndata in cases where we determine that the risk can't be mitigated by rotating\naffected credentials.\" A fork keeps its copy either way, and GitHub cannot give\nyou the fork owner's contact details.",[10,438,439],{},"So the order they describe is the practical one: revoke the key, then decide\nwhether rewriting the history is worth the side effects. Revoking reaches copies\na rewrite cannot, including the one in a clone you do not know about and the one\nin a screenshot somebody kept.",[95,441],{"alt":442,"caption":443,"src":444},"Three identical faded documents, a fork, somebody's clone and a cached view, each one still holding the same amber key. A single teal line runs across the picture and strikes through all three keys.","The same key, in three copies you cannot delete. Revoking it crosses out all three at once.","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fone-line-covers-every-copy-1600x520.png",[17,446,448],{"id":447},"keeping-the-next-one-out-of-the-bundle","Keeping the next one out of the bundle",[10,450,451,454],{},[28,452,453],{},"A key gets into your bundle through a deploy, and deploys keep happening.","\nEvery one of them is another chance for a value you put in a secrets panel to\nend up in a file the browser downloads, which is why a check you ran last month\ndescribes last month's app.",[10,456,457],{},[28,458,459,462],{},[52,460,461],{"href":62},"Our free scan"," answers the question you came here with.",[310,464,465,468,471],{},[313,466,467],{},"the nine checks against your live URL, in about 20 seconds",[313,469,470],{},"every key it can see from outside, each one classified rather than just\nmatched",[313,472,473],{},"a grade and the findings, with no account",[10,475,476],{},[28,477,478,482],{},[52,479,481],{"href":480},"\u002Fpricing","Reeve Monitor"," runs those checks again without you asking.",[310,484,485,488,495,498],{},[313,486,487],{},"all nine checks every hour, on up to three apps",[313,489,490,491,494],{},"a message when a result ",[28,492,493],{},"changes",", so the key that went out in last night's\ndeploy does not wait for you to look",[313,496,497],{},"whether the app is up, every 60 seconds",[313,499,500],{},"a monthly report of what it saw",[10,502,503],{},[28,504,505,509],{},[52,506,508],{"href":507},"\u002Fsupabase-backups","Reeve Care"," keeps a copy of your Supabase database, for\nthe keys that can write.",[310,511,512,515,518,521,524],{},[313,513,514],{},"an encrypted copy every night, kept where your project cannot reach it",[313,516,517],{},"each copy verified before it counts, by counting the rows in every table",[313,519,520],{},"a one-click restore when you need one",[313,522,523],{},"your uploaded files as well, once you connect a Storage credential",[313,525,526],{},"everything Monitor does",[10,528,529,530,532,533,537],{},"A leaked key that can only read leaves your data where it was. A ",[39,531,77],{},"\nkey, or an AWS key with write permissions, can empty a table, and no amount of\nrotating afterwards brings the rows back.\n",[52,534,536],{"href":535},"\u002Fblog\u002Fai-agent-deleted-my-database","The day an AI agent deleted a production database","\nis what that looks like from the inside.",[17,539,541],{"id":540},"what-to-do-right-now","What to do right now",[543,544,545],"key-takeaways",{},[310,546,547,558,561,570,573,576],{},[313,548,549,550,46,552,554,555,557],{},"Identify the key before you touch it. A Supabase ",[39,551,49],{},[39,553,45],{}," key and a Stripe ",[39,556,41],{}," key belong where they are, and of the 1,332 apps where we found a key worth flagging, 1,142 had a Google API key, which wants a restriction instead of a rotation.",[313,559,560],{},"Ask whether it has a meter. If somebody else's requests land on your bill, stop the key now and let the feature break.",[313,562,563,564,566,567,569],{},"Use the stop control as well as the replace one: ",[28,565,327],{}," at Stripe, ",[28,568,345],{}," at AWS, a website and API restriction at Google.",[313,571,572],{},"Then replace it inside the provider's window. Stripe gives you seven days with both keys live, Google accepts both while you migrate, and AWS lets you hold two access keys at once.",[313,574,575],{},"Read the provider log for the window between the deploy and the stop, and write down what you found, including \"nothing\".",[313,577,578],{},"Leave the git history until last. Once the key is revoked, GitHub's own guidance says rewriting history may not be warranted at all.",[10,580,581,582,586],{},"If you would rather work through the whole thing as a list, the\n",[52,583,585],{"href":584},"\u002Fchecklist","10-minute security checklist"," covers this and the other things\nworth switching off in a newly launched app.",{"title":588,"searchDepth":589,"depth":589,"links":590},"",3,[591,593,594,595,596,597,598,599,600],{"id":19,"depth":592,"text":20},2,{"id":67,"depth":592,"text":68},{"id":102,"depth":592,"text":103},{"id":304,"depth":592,"text":305},{"id":370,"depth":592,"text":371},{"id":390,"depth":592,"text":391},{"id":426,"depth":592,"text":427},{"id":447,"depth":592,"text":448},{"id":540,"depth":592,"text":541},"Security basics","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcover-1200x630.png","A credential card carrying a key, standing in front of a control panel whose first switch has been thrown down into the off position.","An API key leaked and you want to know what to do first. Not every key in your frontend is one, and the order matters more than the speed.",false,"md",[608,610,613,616,619],{"q":68,"a":609},"Work out whether the key can spend money. A key that bills you per request is costing you something right now, so stop it immediately and accept that the feature using it breaks for a few minutes. A key that only reads data has already been read if it was public, so take the time to do the replacement in an order that does not lock you out of your own app.",{"q":611,"a":612},"Should I revoke or rotate first?","Revoke first if the key has a meter on it. Stopping the old key and issuing a new one are two separate controls at every provider, and only the first one closes the hole. The exception is a key you can restrict instead: Google tells you to try a website and API restriction before you rotate a Google API key at all.",{"q":614,"a":615},"How do I tell if someone used my key?","Narrow the window to between the deploy that shipped the key and the moment you stopped it, then read the provider log for that range. Stripe shows request logs per key, AWS shows a last-used date on the access key and records calls in CloudTrail, Google charts usage per key, and Supabase keeps API and database logs. You are looking for requests to things your app never touches and traffic at hours nobody was using it. You often cannot be certain, and that is a normal outcome.",{"q":617,"a":618},"Will rotating my key break my app?","Usually not, if you use the window the provider gives you. Stripe keeps the old and new key working together for up to seven days. Google issues the new key with the old key restrictions and accepts both while you migrate. AWS lets you hold two access keys at once so the new one is live before the old one goes. The exception is an older Supabase project, where the switch that disables the legacy keys takes your frontend key with it.",{"q":620,"a":621},"The key is in my git history. Is deleting the file enough?","No, and rewriting the history is probably not the answer either. GitHub documentation says to revoke or rotate the secret first, and that once you have, going through the extra steps to rewrite history may not be warranted. A force push does not reach the copies in forks and clones, or the cached views reachable by commit hash. Making the key useless covers every copy at once.","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",[624,625,626,627,628,629,630,631],"api key leaked what to do","leaked api key","api key exposed what now","my api key is public","rotate leaked api key","api key compromised","what happens if someone gets my api key","found my api key on github",{},true,"Your API key leaked. What to do, in order","\u002Fblog\u002Fapi-key-leaked-what-to-do","2026-10-09",{"title":5,"description":604},"blog\u002Fapi-key-leaked-what-to-do",[640,641,642,643],"If an API key leaked, what to do first depends on which key it is. Publishable keys belong in your frontend and need nothing at all.","For a real secret, work out whether the key can spend money. That is the only part of this with a clock on it.","Stopping a key and replacing a key are two different controls at every provider, and most of them give you a window where both keys work.","Then read the provider log for the window the key was out, and leave your git history alone until the key is dead.","7_e2sR0b4lt0VV3uKQiSF7caI8jqlFUU_YOrB3eMxJQ",[646,652,658,659,665,671,677,683,689,695,701,707,713,719,726,732,738,743,749,755,761,767,773,779,784,790,796,802,808,814,820,826,832,838,844,849,855,861,867,873,878,884,890,896,902,908,914,920,926,932,938,944,949,954,960,965,971,977,983,988,994],{"path":647,"title":648,"description":649,"published":650,"category":601,"image":651,"draft":605},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" on upload","\"New row violates row-level security policy for table objects\" means your upload has no insert rule. Making the bucket public does not add one.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":653,"title":654,"description":655,"published":656,"category":601,"image":657,"draft":605},"\u002Fblog\u002Fbase44-security-scan","Base44 security scan: the one thing only it can see","The Base44 security scan checks seven kinds of problem from inside your app. Here is the half it reads that nothing outside can, and the half it never looks at.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":635,"title":5,"description":604,"published":636,"category":601,"image":622,"draft":605},{"path":660,"title":661,"description":662,"published":663,"category":601,"image":664,"draft":605},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" without disabling RLS","\"Infinite recursion detected in policy for relation\" means your Supabase policy asked the table it protects. Here is how to break the circle.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":666,"title":667,"description":668,"published":669,"category":601,"image":670,"draft":605},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" in Supabase, and the wrong fix","\"No API key found in request\" means your Supabase request arrived without a key. Most answers you find point at your database rules instead.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":672,"title":673,"description":674,"published":675,"category":601,"image":676,"draft":605},"\u002Fblog\u002Fis-base44-safe","Is Base44 safe? What 5,442 live Base44 apps showed","Is Base44 safe? We ran nine checks on 5,442 live Base44 apps. Almost all scored B for reasons the owner never chose, and one question nobody can answer.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":678,"title":679,"description":680,"published":681,"category":601,"image":682,"draft":605},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Hide an API key: move it to a Supabase Edge Function","Hiding an API key means moving it off the browser, and a Supabase Edge Function is the smallest place to put it. Two steps around the move matter more.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":684,"title":685,"description":686,"published":687,"category":601,"image":688,"draft":605},"\u002Fblog\u002Fenv-file-exposed-on-your-server","Is your .env file exposed? The twelve paths to check","Is your .env file exposed on your own web server? Twelve addresses tell you in a minute, and a hit means everything in the file is already public.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":690,"title":691,"description":692,"published":693,"category":601,"image":694,"draft":605},"\u002Fblog\u002Fis-v0-safe","v0 security: all 1,790 v0 apps we scanned got an A","v0 security, measured on 1,790 live v0 apps: every one graded A. Only 17 named a database, and that is most of what the A is measuring.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":696,"title":697,"description":698,"published":699,"category":601,"image":700,"draft":605},"\u002Fblog\u002Fis-bolt-safe","Is Bolt safe? What 1,123 live Bolt apps showed","Is Bolt safe? We ran nine checks on 1,123 live Bolt apps. The hosting came back clean. The findings were API keys and open tables inside the apps.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":702,"title":703,"description":704,"published":705,"category":601,"image":706,"draft":605},"\u002Fblog\u002Fis-supabase-down-or-your-app","Is Supabase down, or is it your app? How to tell","Is Supabase down, or is it your app? A paused project, a full database and a bug of your own all look like an outage. Four checks tell them apart.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":708,"title":709,"description":710,"published":711,"category":601,"image":712,"draft":605},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","Does Supabase encrypt my data? Yes. Here is what it stops","Does Supabase encrypt data? Yes: AES-256 at rest, TLS in transit, SOC 2 and ISO 27001 audited. What each one covers, and the leak none of them stops.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":714,"title":715,"description":716,"published":717,"category":601,"image":718,"draft":605},"\u002Fblog\u002Fsupabase-free-plan-limits","Supabase free plan limits, and what happens at each one","The Supabase free plan limits you to 500 MB of database and 5 GB of egress, with no backups. What happens as you cross each line, and what $25 changes.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":720,"title":721,"description":722,"published":723,"category":724,"image":725,"draft":605},"\u002Fblog\u002Ftest-your-supabase-backup","Test your Supabase backup before the day you need it","How to test your Supabase backup: restore it into a spare project, compare the row counts, sign in, and check for the line a cut-off file is missing.","2026-09-28","Backups","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":727,"title":728,"description":729,"published":730,"category":724,"image":731,"draft":605},"\u002Fblog\u002Fsupabase-backup-github-action","Free Supabase backup with a GitHub Action, and the catch","A Supabase backup GitHub Action costs nothing and suits a lot of apps. The workflow, the connection string that works on GitHub, and the egress each run uses.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":733,"title":734,"description":735,"published":736,"category":724,"image":737,"draft":605},"\u002Fblog\u002Fdownload-your-supabase-backup","Why you can't download your Supabase backup","You can't download your Supabase backup on a current project, because the daily copy is a physical snapshot. How to tell, and how to hold a copy of your own.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":739,"title":740,"description":741,"published":736,"category":601,"image":742,"draft":605},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": the missing grant","From October 30, a new Supabase table answers \"permission denied for table\" until you grant access. The grant the email shows is half the fix.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":744,"title":745,"description":746,"published":747,"category":601,"image":748,"draft":605},"\u002Fblog\u002Fopen-api-endpoint-exposed","Is an open API endpoint a security problem? Look at the JSON","Your scan flagged an open API endpoint. Whether it matters depends on what came back, and most of the ones we found were the platform's own.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":750,"title":751,"description":752,"published":753,"category":601,"image":754,"draft":605},"\u002Fblog\u002Flovable-security-scan","Lovable security scan: the one thing it cannot prove","Lovable security scan: what the Quick and Deep scans check, when each one runs, and the one thing no scan from inside your project can prove.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":756,"title":757,"description":758,"published":759,"category":724,"image":760,"draft":605},"\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","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":762,"title":763,"description":764,"published":765,"category":601,"image":766,"draft":605},"\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":768,"title":769,"description":770,"published":771,"category":601,"image":772,"draft":605},"\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":774,"title":775,"description":776,"published":777,"category":601,"image":778,"draft":605},"\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":294,"title":780,"description":781,"published":782,"category":601,"image":783,"draft":605},"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":785,"title":786,"description":787,"published":788,"category":601,"image":789,"draft":605},"\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":791,"title":792,"description":793,"published":794,"category":601,"image":795,"draft":605},"\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":797,"title":798,"description":799,"published":800,"category":601,"image":801,"draft":605},"\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":803,"title":804,"description":805,"published":806,"category":601,"image":807,"draft":605},"\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":809,"title":810,"description":811,"published":812,"category":724,"image":813,"draft":605},"\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":815,"title":816,"description":817,"published":818,"category":724,"image":819,"draft":605},"\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":821,"title":822,"description":823,"published":824,"category":724,"image":825,"draft":605},"\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":827,"title":828,"description":829,"published":830,"category":601,"image":831,"draft":605},"\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":833,"title":834,"description":835,"published":836,"category":601,"image":837,"draft":605},"\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":839,"title":840,"description":841,"published":842,"category":601,"image":843,"draft":605},"\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":299,"title":845,"description":846,"published":847,"category":601,"image":848,"draft":605},"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":850,"title":851,"description":852,"published":853,"category":601,"image":854,"draft":605},"\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":856,"title":857,"description":858,"published":859,"category":601,"image":860,"draft":605},"\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":862,"title":863,"description":864,"published":865,"category":601,"image":866,"draft":605},"\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":868,"title":869,"description":870,"published":871,"category":601,"image":872,"draft":605},"\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":285,"title":874,"description":875,"published":876,"category":601,"image":877,"draft":605},"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":879,"title":880,"description":881,"published":882,"category":601,"image":883,"draft":605},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","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":885,"title":886,"description":887,"published":888,"category":724,"image":889,"draft":605},"\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":891,"title":892,"description":893,"published":894,"category":601,"image":895,"draft":605},"\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":897,"title":898,"description":899,"published":900,"category":724,"image":901,"draft":605},"\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":903,"title":904,"description":905,"published":906,"category":601,"image":907,"draft":605},"\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":909,"title":910,"description":911,"published":912,"category":601,"image":913,"draft":605},"\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":915,"title":916,"description":917,"published":918,"category":601,"image":919,"draft":605},"\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":921,"title":922,"description":923,"published":924,"category":601,"image":925,"draft":605},"\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":927,"title":928,"description":929,"published":930,"category":724,"image":931,"draft":605},"\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":933,"title":934,"description":935,"published":936,"category":724,"image":937,"draft":605},"\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":939,"title":940,"description":941,"published":942,"category":601,"image":943,"draft":605},"\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":290,"title":945,"description":946,"published":947,"category":601,"image":948,"draft":605},"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":535,"title":950,"description":951,"published":952,"category":724,"image":953,"draft":605},"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":955,"title":956,"description":957,"published":958,"category":601,"image":959,"draft":605},"\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":961,"title":962,"description":963,"published":958,"category":601,"image":964,"draft":605},"\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":966,"title":967,"description":968,"published":969,"category":601,"image":970,"draft":605},"\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":972,"title":973,"description":974,"published":975,"category":724,"image":976,"draft":605},"\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":978,"title":979,"description":980,"published":981,"category":601,"image":982,"draft":605},"\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":984,"title":985,"description":986,"published":981,"category":724,"image":987,"draft":605},"\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":989,"title":990,"description":991,"published":992,"category":724,"image":993,"draft":605},"\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":54,"title":995,"description":996,"published":992,"category":601,"image":997,"draft":605},"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",1791705957970]