[{"data":1,"prerenderedAt":662},["ShallowReactive",2],{"blog-en-missing-security-headers":3,"blog-index-en":417},{"id":4,"title":5,"body":6,"category":373,"cover":374,"coverAlt":375,"description":376,"draft":377,"extension":378,"faq":379,"image":395,"keywords":396,"meta":404,"navigation":405,"ogTitle":406,"path":407,"published":408,"seo":409,"stem":410,"tldr":411,"updated":408,"__hash__":416},"blog_en\u002Fblog\u002Fmissing-security-headers.md","Missing security headers: when it actually matters",{"type":7,"value":8,"toc":361},"minimark",[9,13,21,30,35,38,41,44,47,51,54,57,63,70,73,76,83,87,90,179,192,196,199,202,207,210,215,227,231,234,237,252,261,265,268,283,293,296,308,312,348],[10,11,12],"p",{},"Your scan comes back and nearly every line is green. One is amber: some browser\nsafety headers are missing. It is the only thing on the page with a colour, so\nit reads like the thing to deal with first.",[10,14,15,16,20],{},"Here is the part most advice on this gets wrong. ",[17,18,19],"strong",{},"Missing security headers is\nthe most common finding we print, and on its own it tells you almost nothing\nabout whether anyone can reach your data."," Treating it like a breach means\neither fixing it in a panic ahead of the things that actually decide who reads\nyour database, or learning that amber lines are noise, which is worse.",[10,22,23,24,29],{},"Between 12 and 14 August 2026 we ran the same nine external checks over 30,998\nlive apps built with Lovable, Bolt, v0, Replit and Base44. Of the apps this\ncheck got an answer from, 30,756 out of 30,981 were missing at least one header.\nThe full numbers are in\n",[25,26,28],"a",{"href":27},"\u002Fresearch\u002Fvibe-coded-app-security-2026","our scan report",".",[31,32,34],"h2",{"id":33},"are-missing-security-headers-a-problem","Are missing security headers a problem?",[10,36,37],{},"It is a real gap and it is almost always the least urgent line on your report.",[10,39,40],{},"A security header is a short instruction your server attaches to every page it\nsends, addressed to the visitor's browser rather than to the visitor. Don't let\nanother website put this page inside a frame. Don't guess what kind of file this\nis. Don't pass my full address along when someone clicks a link away. They are\nnotes to a helper that will do whatever any website tells it.",[10,42,43],{},"The rest of your report is about locks. Whether Row Level Security is on, whether\na secret key is sitting in your code, whether a storage bucket lists its\ncontents: those decide who can reach your data at all. Notes and locks are both\nworth having, and the note you never wrote is not how people lose a database.",[10,45,46],{},"Our grading treats it that way. A missing header takes five points off a\nhundred and caps nothing, so an app whose only finding is this one comes back at\n95 and a grade A.",[31,48,50],{"id":49},"what-we-found-across-30998-apps","What we found across 30,998 apps",[10,52,53],{},"Missing headers turned up almost everywhere, and it barely moved with anything\nelse on the report.",[10,55,56],{},"25,821 of those apps got an answer from all nine checks, which is the group\nwhere \"nothing else was wrong\" means something. Among them, 25,606 were missing\nat least one header and 215 had all five.",[58,59],"diagram",{"alt":60,"caption":61,"src":62},"A grid of four counts. The rows are the header finding, drawn as a filled bar for present and an empty dashed bar for missing. The columns are the rest of the report, a tick for nothing found and a cross for something found. Top row: 70 and 145. Bottom row: 17,043 and 8,563.","Apps missing headers were no more likely to have anything else wrong than apps that had all five.","\u002Fblog\u002Fmissing-security-headers\u002Fdoes-it-predict-anything-1600x900.png",[10,64,65,66,69],{},"Of the 25,606 apps missing at least one header, ",[17,67,68],{},"17,043 had nothing else wrong\nat all",". Two in three. It was the only problem on their report.",[10,71,72],{},"Now read the other row, which is the part that surprised us. Of the 215 apps\nthat had all five headers set, 145 had something else wrong. That is a higher\nrate of real problems than among the apps missing headers, not a lower one.",[10,74,75],{},"We did not measure why, and the honest reading is narrow: whatever else these\nscans found, the header finding did not predict it. An app with a perfect set of\nheaders was not a safer app in our data. The likeliest explanation is that\nsetting headers at all means somebody configured a real host, which tends to\nmean a bigger app with more to get wrong, but that is a guess and we have not\ntested it.",[10,77,78,79,29],{},"If you want to see which of these lines your own app produces, the scan is free,\ntakes about 20 seconds and needs no account:\n",[25,80,82],{"href":81},"\u002Fsecurity-scanner","scan your app",[31,84,86],{"id":85},"what-each-header-actually-does","What each header actually does",[10,88,89],{},"Each one turns off a browser behaviour that is on by default.",[91,92,93,109],"table",{},[94,95,96],"thead",{},[97,98,99,103,106],"tr",{},[100,101,102],"th",{},"Header",[100,104,105],{},"What it tells the browser",[100,107,108],{},"What its absence allows",[110,111,112,127,140,153,166],"tbody",{},[97,113,114,121,124],{},[115,116,117],"td",{},[118,119,120],"code",{},"Content-Security-Policy",[115,122,123],{},"Which places this page is allowed to load code and styles from",[115,125,126],{},"A script that gets injected can send data anywhere it likes",[97,128,129,134,137],{},[115,130,131],{},[118,132,133],{},"Strict-Transport-Security",[115,135,136],{},"Always come back over HTTPS, never plain HTTP",[115,138,139],{},"A visit on an untrusted network can be pushed down to plain HTTP",[97,141,142,147,150],{},[115,143,144],{},[118,145,146],{},"X-Frame-Options",[115,148,149],{},"Don't let another site display this page inside a frame",[115,151,152],{},"Your page can be loaded invisibly inside somebody else's",[97,154,155,160,163],{},[115,156,157],{},[118,158,159],{},"X-Content-Type-Options",[115,161,162],{},"Trust the file type I gave you and don't guess from the contents",[115,164,165],{},"A file someone uploaded can be served as a different kind of file",[97,167,168,173,176],{},[115,169,170],{},[118,171,172],{},"Referrer-Policy",[115,174,175],{},"How much of this address to pass on when a visitor clicks away",[115,177,178],{},"Full URLs, including anything sitting in them, reach third-party sites",[10,180,181,182,184,185,188,189,191],{},"Two of those overlap, which the scanner accounts for. A\n",[118,183,120],{}," containing ",[118,186,187],{},"frame-ancestors"," does the same job as\n",[118,190,146],{},", so our check counts either one and does not ask for both.",[31,193,195],{"id":194},"when-a-missing-header-is-the-whole-problem","When a missing header is the whole problem",[10,197,198],{},"When your app has something worth clicking on while signed in.",[10,200,201],{},"That is the clickjacking case, and it is the one that needs no other mistake to\nwork. Someone loads your app in an invisible frame on a page they control and\nputs their own buttons over yours, so a visitor who is already signed in clicks\nwhat looks like their page and hits a control of yours instead: the button that\ndeletes an account, approves a transfer or changes an email address.",[58,203],{"alt":204,"caption":205,"src":206},"Two rows. In the first, a page travels to the right and is stopped by a bar before reaching a dashed empty pane. In the second there is no bar, and the page is drawn small inside the other pane with a mouse pointer over its button.","With the header, the browser refuses to draw your page inside another site. Without it, that is a supported thing to do.","\u002Fblog\u002Fmissing-security-headers\u002Floaded-inside-another-page-1600x790.png",[10,208,209],{},"The visitor is signed in, so the action carries their session. Nothing about\nyour database has to be misconfigured. What makes it work is that the browser\nwas never told to refuse.",[10,211,212,214],{},[118,213,172],{}," has a smaller version of the same shape. If a signed-in page\nof yours has anything identifying in the URL, and it links out to an image host\nor an analytics script, the whole address travels with the click. Whoever runs\nthat other server sees where your user was.",[10,216,217,218,220,221,223,224,226],{},"The other three are conditional. They matter when something else has already\ngone wrong, and their job is to keep it small. A ",[118,219,120],{},"\nlimits what an injected script can do with its access. ",[118,222,159],{},"\nmatters if strangers can upload files to you. ",[118,225,133],{}," covers\nsomeone using your app on a network they don't control.",[31,228,230],{"id":229},"do-i-need-security-headers","Do I need security headers?",[10,232,233],{},"Yes. They are cheap, and they go after the findings that decide who can read\nyour database.",[10,235,236],{},"The order that follows from the measurement above: anything critical or high on\nyour report first, headers after. If your report has only this one line on it,\nas it did for 17,043 of the apps we scanned, then headers are the top of your\nlist by default.",[10,238,239,240,243,244,247,248,251],{},"Two of the five cost nothing to get right. ",[118,241,242],{},"X-Content-Type-Options: nosniff"," and\n",[118,245,246],{},"Referrer-Policy: strict-origin-when-cross-origin"," are one line each, have no\nwrong value to choose and cannot break a working app. ",[118,249,250],{},"X-Frame-Options: DENY"," is\nalso one line, and worth checking first if you deliberately embed your own app\nsomewhere.",[10,253,254,256,257,260],{},[118,255,120],{}," is the one that takes real work. A first attempt\nusually blocks something your own page needed, and the symptom is a feature that\nsilently stops working. Ship it as ",[118,258,259],{},"Content-Security-Policy-Report-Only",", which\ntells you what it would have blocked without blocking anything, and read a\nweek's reports before turning it on.",[31,262,264],{"id":263},"how-to-add-them","How to add them",[10,266,267],{},"In whatever serves your app to the internet, which is often not the place you\nbuild it.",[10,269,270,271,274,275,278,279,282],{},"Most builders deploy to a host that owns the response, so the setting lives\nthere. On Vercel it is ",[118,272,273],{},"headers"," in ",[118,276,277],{},"vercel.json",". On Netlify and Cloudflare\nPages it is a ",[118,280,281],{},"_headers"," file at the root of your published output. Behind\nCloudflare's proxy it is a Transform Rule. If you run your own server, it is\nyour nginx or Caddy config.",[284,285,287],"callout",{"type":286},"warn",[10,288,289,292],{},[17,290,291],{},"If your app is on your builder's own subdomain, you may not be able to set\nthese at all."," The platform sends the response, the headers are its choice, and\nthere is no file you can add that overrides them. That is a real answer, not a\nfailure to look hard enough. Connecting a custom domain is what gives you a host\nof your own to configure, and until you want one, this finding is fine to leave\nsitting on your report.",[10,294,295],{},"The thing to know about headers once you have set them is that they come off\nwithout anyone touching them. Move to a custom domain, put a proxy in front,\nswitch hosts, change a framework config: the headers you added live in one of\nthose places, and any of those moves can leave them behind. Nothing tells you.\nThe page keeps working, and the setting is gone.",[10,297,298,299,303,304,29],{},"That is the sort of thing ",[25,300,302],{"href":301},"\u002F#pricing","Reeve Monitor"," exists for. It re-runs the\nfull check every hour on up to three apps, watches uptime every 60 seconds, and\ntells you when a result changes rather than waiting for you to look. Monitor\nwatches and nothing more, so if you also want a copy of your database kept\nsomewhere your builder cannot reach it,\n",[25,305,307],{"href":306},"\u002Fsupabase-backups","that is Care, and it covers Supabase",[31,309,311],{"id":310},"what-to-do-right-now","What to do right now",[313,314,315],"key-takeaways",{},[316,317,318,322,331,336,342,345],"ul",{},[319,320,321],"li",{},"Read the rest of your report first. If anything is marked critical or high, that is the work, and headers wait.",[319,323,324,325,327,328,330],{},"Add ",[118,326,242],{}," and ",[118,329,246],{}," today. One line each, nothing to decide, nothing to break.",[319,332,324,333,335],{},[118,334,250],{}," if people sign in to your app and can do something consequential in one click.",[319,337,338,339,341],{},"Leave ",[118,340,120],{}," until last, and start it in report-only mode so you find out what it breaks before your visitors do.",[319,343,344],{},"If you are on a builder subdomain with no header setting, leave the finding alone. It is not yours to fix yet.",[319,346,347],{},"Check them again after you move domain, add a proxy or change hosts. That is when they disappear.",[10,349,350,351,355,356,360],{},"Headers are worth a quiet afternoon once the rest of the report is clean. If you\nwould rather work through the whole list in order, the\n",[25,352,354],{"href":353},"\u002Fchecklist","10-minute security checklist"," covers what to switch off in a newly\nlaunched app, and\n",[25,357,359],{"href":358},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","the findings that do decide who reads your database","\nare the ones to clear first.",{"title":362,"searchDepth":363,"depth":363,"links":364},"",3,[365,367,368,369,370,371,372],{"id":33,"depth":366,"text":34},2,{"id":49,"depth":366,"text":50},{"id":85,"depth":366,"text":86},{"id":194,"depth":366,"text":195},{"id":229,"depth":366,"text":230},{"id":263,"depth":366,"text":264},{"id":310,"depth":366,"text":311},"Security basics","\u002Fblog\u002Fmissing-security-headers\u002Fcover-1200x630.png","The list of headers a site sends back with every page, four of its rows ruled and empty where a value would go.","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.",false,"md",[380,383,386,389,392],{"q":381,"a":382},"My scan says security headers are missing. Has my app been hacked?","No. A missing header is a setting that was never switched on, and it is not evidence that anything happened. Nothing about the finding involves anyone reaching your data or your account. It describes an instruction your site could be giving visiting browsers and currently is not.",{"q":384,"a":385},"Which security header should I add first?","X-Content-Type-Options and Referrer-Policy, because both are a single line, neither has a wrong value to pick, and neither can break a working app. X-Frame-Options next if people sign in to your app. Strict-Transport-Security after that. Content-Security-Policy last, because it is the only one of the five that takes real thought and the only one that can stop your own scripts from running.",{"q":387,"a":388},"I am on my builder default domain and there is no setting for headers. What now?","Leave them. When your app is served from a platform subdomain, the response headers are the platform choice and not yours, and there is no file you can add that changes them. Connect a custom domain if you want control of this, because the domain is what lets you put your own host or a proxy in front of the app. Until then, spend the effort on the findings that are yours to fix.",{"q":390,"a":391},"Can a Content-Security-Policy break my app?","Yes, and this is the normal outcome of a first attempt. A policy that does not allow inline scripts will stop inline scripts, including ones your builder generated, and the page goes blank or loses a feature with an error only in the browser console. Start with Content-Security-Policy-Report-Only, which reports what it would have blocked and blocks nothing, then read the reports for a week before you switch it on for real.",{"q":393,"a":394},"Do security headers help if my database is readable by anyone?","No. Headers are instructions to the browsers visiting your site, and someone reading your database directly is not using a browser or visiting your site. That request goes straight to your database provider and never touches your pages, so no header on them applies to it. Row Level Security is what decides that one.","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",[397,398,399,400,401,402,403],"missing security headers","content-security-policy missing","x-frame-options missing","do i need security headers","security headers scan","strict-transport-security missing","are missing security headers a problem",{},true,"Missing security headers: when it matters","\u002Fblog\u002Fmissing-security-headers","2026-09-04",{"title":5,"description":376},"blog\u002Fmissing-security-headers",[412,413,414,415],"Missing security headers is the most common finding there is, and on its own it says almost nothing about whether anyone can reach your data.","We scanned 30,998 live apps. Two in three of the ones missing headers had nothing else wrong at all.","It is still worth fixing, and it belongs after the things that decide who can read your database.","On some builder domains you cannot set them yourself, and leaving them is a reasonable answer.","j6SOjJtkMaQNYpT2Qq19Ooryad9Vltjk7szSAOBR8HA",[418,425,431,437,443,449,455,461,467,473,479,485,491,497,503,509,515,521,527,533,534,540,546,552,558,564,570,576,582,588,594,600,606,612,618,623,628,634,640,646,651,657],{"path":419,"title":420,"description":421,"published":422,"category":423,"image":424,"draft":377},"\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":426,"title":427,"description":428,"published":429,"category":373,"image":430,"draft":377},"\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":432,"title":433,"description":434,"published":435,"category":373,"image":436,"draft":377},"\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":438,"title":439,"description":440,"published":441,"category":373,"image":442,"draft":377},"\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":444,"title":445,"description":446,"published":447,"category":373,"image":448,"draft":377},"\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":450,"title":451,"description":452,"published":453,"category":373,"image":454,"draft":377},"\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":456,"title":457,"description":458,"published":459,"category":373,"image":460,"draft":377},"\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":462,"title":463,"description":464,"published":465,"category":373,"image":466,"draft":377},"\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":468,"title":469,"description":470,"published":471,"category":373,"image":472,"draft":377},"\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":474,"title":475,"description":476,"published":477,"category":423,"image":478,"draft":377},"\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":480,"title":481,"description":482,"published":483,"category":423,"image":484,"draft":377},"\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":486,"title":487,"description":488,"published":489,"category":423,"image":490,"draft":377},"\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":492,"title":493,"description":494,"published":495,"category":373,"image":496,"draft":377},"\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":498,"title":499,"description":500,"published":501,"category":373,"image":502,"draft":377},"\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":504,"title":505,"description":506,"published":507,"category":373,"image":508,"draft":377},"\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":510,"title":511,"description":512,"published":513,"category":373,"image":514,"draft":377},"\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":516,"title":517,"description":518,"published":519,"category":373,"image":520,"draft":377},"\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":522,"title":523,"description":524,"published":525,"category":373,"image":526,"draft":377},"\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":528,"title":529,"description":530,"published":531,"category":373,"image":532,"draft":377},"\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":407,"title":5,"description":376,"published":408,"category":373,"image":395,"draft":377},{"path":535,"title":536,"description":537,"published":538,"category":373,"image":539,"draft":377},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","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":541,"title":542,"description":543,"published":544,"category":373,"image":545,"draft":377},"\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":547,"title":548,"description":549,"published":550,"category":423,"image":551,"draft":377},"\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":553,"title":554,"description":555,"published":556,"category":373,"image":557,"draft":377},"\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":559,"title":560,"description":561,"published":562,"category":423,"image":563,"draft":377},"\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":565,"title":566,"description":567,"published":568,"category":373,"image":569,"draft":377},"\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":571,"title":572,"description":573,"published":574,"category":373,"image":575,"draft":377},"\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":577,"title":578,"description":579,"published":580,"category":373,"image":581,"draft":377},"\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":583,"title":584,"description":585,"published":586,"category":373,"image":587,"draft":377},"\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":589,"title":590,"description":591,"published":592,"category":423,"image":593,"draft":377},"\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":595,"title":596,"description":597,"published":598,"category":423,"image":599,"draft":377},"\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":601,"title":602,"description":603,"published":604,"category":373,"image":605,"draft":377},"\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":607,"title":608,"description":609,"published":610,"category":373,"image":611,"draft":377},"\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":613,"title":614,"description":615,"published":616,"category":423,"image":617,"draft":377},"\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":358,"title":619,"description":620,"published":621,"category":373,"image":622,"draft":377},"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":624,"title":625,"description":626,"published":621,"category":373,"image":627,"draft":377},"\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":629,"title":630,"description":631,"published":632,"category":373,"image":633,"draft":377},"\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":635,"title":636,"description":637,"published":638,"category":423,"image":639,"draft":377},"\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":641,"title":642,"description":643,"published":644,"category":373,"image":645,"draft":377},"\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":647,"title":648,"description":649,"published":644,"category":423,"image":650,"draft":377},"\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":652,"title":653,"description":654,"published":655,"category":423,"image":656,"draft":377},"\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":658,"title":659,"description":660,"published":655,"category":373,"image":661,"draft":377},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","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]