[{"data":1,"prerenderedAt":1221},["ShallowReactive",2],{"blog-en-test-your-supabase-backup":3,"blog-index-en":868},{"id":4,"title":5,"body":6,"category":823,"cover":824,"coverAlt":825,"description":826,"draft":827,"extension":828,"faq":829,"image":845,"keywords":846,"meta":855,"navigation":856,"ogTitle":857,"path":858,"published":859,"seo":860,"stem":861,"tldr":862,"updated":866,"__hash__":867},"blog_en\u002Fblog\u002Ftest-your-supabase-backup.md","Test your Supabase backup before the day you need it",{"type":7,"value":8,"toc":807},"minimark",[9,13,21,24,29,32,35,39,42,146,155,161,165,172,186,211,221,238,243,247,250,275,301,319,329,332,347,351,354,429,437,470,474,477,483,491,494,504,510,513,574,578,584,590,596,617,623,627,635,638,644,647,651,658,673,676,680,683,686,689,693,698,747,759,763,795],[10,11,12],"p",{},"Somewhere there is a file with your database in it. A GitHub Action writes one\nevery night, or you ran Supabase's three backup commands once, or your plan\ntakes daily copies. It has the right name and about the size you would expect.\nNobody has ever opened it.",[10,14,15,16,20],{},"Here is the part the setup guides leave for later: ",[17,18,19],"strong",{},"a backup that has never\nbeen restored is a claim, not a copy."," The only way to test a Supabase backup\nis to restore it, on purpose, somewhere that does not matter, and look at what\ncomes back. A dump cut off halfway, a dump with no rows in it and a dump of the\nwrong project all sit in storage looking exactly like a good one.",[10,22,23],{},"Think of a fire drill. A building holds one on an ordinary morning, when\nnothing is burning, because the first time anyone walks down those stairs\nshould not be the day they fill with smoke. Outside, somebody counts heads\nagainst the list of who was in, and somebody writes the date on the sheet by\nthe door. A restore drill is the same: walk the route, count, and write it\ndown.",[25,26,28],"h2",{"id":27},"how-do-i-know-my-supabase-backup-actually-works","How do I know my Supabase backup actually works?",[10,30,31],{},"Restore it and look. That is the only test there is, because nothing about the\nfile itself separates a good backup from a broken one.",[10,33,34],{},"The drill takes your newest copy, replays it into a project that holds nothing\nyou care about, and asks three questions of the result. Is every table there,\nwith about as many rows as the live one? Is the newest row from the night the\ncopy was taken? Can you sign in as yourself? A good file answers yes to all\nthree, and every way a backup goes wrong fails at least one of them.",[25,36,38],{"id":37},"five-ways-a-backup-is-wrong-and-still-looks-right","Five ways a backup is wrong and still looks right",[10,40,41],{},"All five of these finish on the night without an error, and all five leave a\nfile with the usual name in the usual place. They only come apart when the file\nis replayed.",[43,44,45,61],"table",{},[46,47,48],"thead",{},[49,50,51,55,58],"tr",{},[52,53,54],"th",{},"What is wrong with the file",[52,56,57],{},"How it usually gets that way",[52,59,60],{},"The step of the drill that catches it",[62,63,64,88,106,124,135],"tbody",{},[49,65,66,70,85],{},[67,68,69],"td",{},"It stops partway through",[67,71,72,73,77,78,81,82,84],{},"A script that pipes ",[74,75,76],"code",{},"pg_dump"," into ",[74,79,80],{},"gzip"," reports what ",[74,83,80],{}," did, so a dump that died halfway is saved and the job says it succeeded. A full disk or an upload that gave up does the same",[67,86,87],{},"The closing line, then the counts",[49,89,90,93,103],{},[67,91,92],{},"It has your tables and none of the rows",[67,94,95,98,99,102],{},[74,96,97],{},"supabase db dump"," was run without ",[74,100,101],{},"--data-only",", which writes the structure on its own",[67,104,105],{},"The counts: every table at zero",[49,107,108,111,117],{},[67,109,110],{},"It has the rows and none of the accounts",[67,112,113,114],{},"The dump named only your own schema, and your users live in one called ",[74,115,116],{},"auth",[67,118,119,120,123],{},"The search for ",[74,121,122],{},"auth.users",", then the sign-in",[49,125,126,129,132],{},[67,127,128],{},"It is another project's data",[67,130,131],{},"The connection string points at a staging copy or an old project",[67,133,134],{},"The newest row, from the wrong day",[49,136,137,140,143],{},[67,138,139],{},"It will not load at all",[67,141,142],{},"A Postgres 17 dump replayed into Postgres 15, or ownership lines the new project refuses",[67,144,145],{},"The restore stops with an error",[10,147,148,149,154],{},"The second and third are the subject of\n",[150,151,153],"a",{"href":152},"\u002Fblog\u002Fsupabase-backup-auth-users","why your Supabase dump has no users in it",",\nand both come from a dump command run with fewer flags than it needed. The\nfirst is the one that surprised us, so it gets a section of its own.",[156,157],"diagram",{"alt":158,"caption":159,"src":160},"Six identical sealed files in a row, each dropping into a restored database. The first holds full tables and a register of people, ticked. Five are crossed: a table cut short beside an empty one, all tables empty, an empty register, greyed rows marked not equal, and a replay stopped at a bar.","In storage all six files look the same. Replayed, only the first gives you back the database you had.","\u002Fblog\u002Ftest-your-supabase-backup\u002Fsix-files-one-good-copy-1600x760.png",[25,162,164],{"id":163},"does-a-backup-file-that-was-cut-off-fail-when-you-restore-it","Does a backup file that was cut off fail when you restore it?",[10,166,167,168,171],{},"Not always. We cut one off partway through a table, replayed it with the flags\nSupabase's own guide uses, and ",[74,169,170],{},"psql"," finished without an error.",[10,173,174,175,177,178,181,182,185],{},"The test was small and easy to repeat. On 27 September 2026 we dumped a\ndatabase of two tables, one with 5,000 rows and one with 300, cut the file at\nthree different points, and replayed each version with ",[74,176,170],{}," from Postgres\n17.11. Every replay used ",[74,179,180],{},"--single-transaction"," and\n",[74,183,184],{},"--variable ON_ERROR_STOP=1",", the two flags that are there to stop a restore at\nthe first problem and undo everything it did.",[187,188,189,199,205],"ul",{},[190,191,192,195,196,198],"li",{},[17,193,194],{},"Cut in the middle of a value",", ",[74,197,170],{}," stopped with an error and wrote\nnothing. That is the outcome you would hope for.",[190,200,201,204],{},[17,202,203],{},"Cut inside the last column of a row",", it finished with an exit code of 0,\nwhich is how a program says it succeeded. The first table came back with\n2,596 of its 5,000 rows, the last of them missing half its text, and the\nsecond table came back empty.",[190,206,207,210],{},[17,208,209],{},"Cut exactly between the two tables",", it also finished with 0. The first\ntable was complete and the second was empty.",[10,212,213,214,217,218,220],{},"The reason is the way a dump stores rows. Each table's rows sit in one block\nthat ends with a line holding only ",[74,215,216],{},"\\.",", and when the file runs out before that\nline, ",[74,219,170],{}," takes the end of the file as the end of the block. The only sign\nthat more was meant to follow is a line that should have been at the very end.",[10,222,223,224,226,227,230,231,234,235,237],{},"That line is ",[74,225,76],{}," signing off. A complete dump has the words\n",[74,228,229],{},"PostgreSQL database dump complete"," near its end, and a dump that was cut off\ndoes not. Of the three files the Supabase CLI writes, the line survives only in\n",[74,232,233],{},"data.sql",", because the CLI strips every comment out of the schema file\n(checked with version 2.111 of the CLI). That makes ",[74,236,233],{}," the file to\nsearch, and the search is the second step of the drill.",[156,239],{"alt":240,"caption":241,"src":242},"A file whose lower edge is torn off feeds a restore step marked ON_ERROR_STOP=1 with a green tick. Beyond it, two tables: the first filled to about half, marked 2596 of 5000, with its last row torn; the second empty, marked 0 of 300.","The replay reported success. The first table came back half full and the second came back empty.","\u002Fblog\u002Ftest-your-supabase-backup\u002Fa-short-file-restores-clean-1600x680.png",[25,244,246],{"id":245},"where-should-i-restore-it","Where should I restore it?",[10,248,249],{},"Into a Supabase project that holds nothing you care about: a spare project in\nyour account, or a copy of Supabase running on your own computer. Never into the\nproject your app uses.",[10,251,252,255,256,262,263,268,269,274],{},[17,253,254],{},"A spare Supabase project"," is the closest thing to your real one, and it is\nthe target ",[150,257,261],{"href":258,"rel":259},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fplatform\u002Fmigrating-within-supabase\u002Fbackup-restore",[260],"nofollow","Supabase's backup and restore guide","\nis written for: its first step is to create a new project. On the free plan you\nare\n",[150,264,267],{"href":265,"rel":266},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fplatform\u002Fbilling-faq",[260],"entitled to two active free projects",",\nand paused ones do not count, so a spare usually costs nothing as long as your\ndatabase fits the free plan. In a paid organization a new project's compute is\n",[150,270,273],{"href":271,"rel":272},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fplatform\u002Fmanage-your-usage\u002Fcompute",[260],"charged by the hour",",\nso delete it when the drill is finished.",[10,276,277,280,281,292,293,296,297,300],{},[17,278,279],{},"Supabase on your own computer"," costs nothing and involves no account. The\nSupabase CLI runs the whole stack locally in Docker:\n",[150,282,285,288,289],{"href":283,"rel":284},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Flocal-development\u002Fcli\u002Fgetting-started",[260],[74,286,287],{},"supabase init",", then ",[74,290,291],{},"supabase start",",\nand it prints a database address on port 54322 and a publishable key for the\nlocal project. Before you start it, open ",[74,294,295],{},"supabase\u002Fconfig.toml"," and set\n",[74,298,299],{},"major_version"," to your project's Postgres major version. The comment above\nthat setting says it has to match, and your project's version is under Project\nSettings, then General.",[10,302,303,304,306,307,310,311,313,314,318],{},"There is one catch on Postgres 15. Unless the backup job set a version, the CLI\ntook the copy with ",[74,305,76],{}," 17, and the data file then carries\n",[74,308,309],{},"SET transaction_timeout = 0"," near its top, a setting Postgres 15 does not\nrecognise, so the replay stops on that line. For the drill, set ",[74,312,299],{},"\nto 17. Then give the backup job the one-line fix in\n",[150,315,317],{"href":316},"\u002Fblog\u002Fsupabase-backup-github-action","the GitHub Action article",", because the\nproject you would restore into for real is still on 15.",[10,320,321,322,324,325,328],{},"A plain Postgres in Docker, with nothing of Supabase's in it, is not enough. A\nSupabase dump refers to things only Supabase creates, such as the ",[74,323,116],{}," schema\nyour users live in and the ",[74,326,327],{},"authenticated"," role your security policies name,\nand the restore stops at the first line that mentions one.",[10,330,331],{},"One caution, because the spare now holds a real copy. It has your users' email\naddresses in it, so do not point your app or its webhooks at it, and delete it\nwhen you are done.",[10,333,334,335,338,339,342,343,346],{},"Scheduled jobs are the part that does not come across. If your project runs them\nthrough the ",[74,336,337],{},"pg_cron"," extension, the CLI's three files bring the extension back\nand none of its jobs, because the jobs are rows in ",[74,340,341],{},"cron.job"," and the data dump\nleaves those rows out. We checked on 4 October 2026 with version 2.119 of the\nCLI, by restoring a database that had a job scheduled. The spare stays quiet as\na result, and a real restore from these files starts with no scheduled jobs\neither, so keep your ",[74,344,345],{},"cron.schedule"," calls somewhere you can run them again.",[25,348,350],{"id":349},"how-to-test-a-supabase-backup-step-by-step","How to test a Supabase backup, step by step",[10,352,353],{},"Six steps, and the first three happen before you restore anything.",[355,356,357,363,386,402,417,423],"ol",{},[190,358,359,362],{},[17,360,361],{},"Find the newest copy and read its date."," If it is older than the last\nscheduled run, the job has stopped, and that is your first finding.",[190,364,365,375,376],{},[17,366,367,368,370,371,374],{},"Search ",[74,369,233],{}," for ",[74,372,373],{},"dump complete","."," One match means the file reached\nits end. None means it was cut off, whatever its size. A text editor's\nsearch works as well as a terminal:",[377,378,383],"pre",{"className":379,"code":381,"language":382},[380],"language-text","grep -c 'dump complete' data.sql\n","text",[74,384,381],{"__ignoreMap":385},"",[190,387,388,391,392,395,396],{},[17,389,390],{},"Search it for your users."," A line beginning ",[74,393,394],{},"COPY \"auth\".\"users\""," with\nrows under it means your accounts are in the file:",[377,397,400],{"className":398,"code":399,"language":382},[380],"grep -n 'COPY .*auth.*users' data.sql\n",[74,401,399],{"__ignoreMap":385},[190,403,404,407,408,374,411],{},[17,405,406],{},"Replay it into the spare"," with the command from Supabase's guide, pointed\nat the spare's connection string. On a local copy of Supabase that string is\n",[74,409,410],{},"postgresql:\u002F\u002Fpostgres:postgres@127.0.0.1:54322\u002Fpostgres",[377,412,415],{"className":413,"code":414,"language":382},[380],"psql \\\n  --single-transaction \\\n  --variable ON_ERROR_STOP=1 \\\n  --file roles.sql \\\n  --file schema.sql \\\n  --command 'SET session_replication_role = replica' \\\n  --file data.sql \\\n  --dbname \"postgresql:\u002F\u002F…the spare's connection string…\"\n",[74,416,414],{"__ignoreMap":385},[190,418,419,422],{},[17,420,421],{},"Count the rows and read the newest one",", in both databases. The next\nsection has the query.",[190,424,425,428],{},[17,426,427],{},"Sign in to the spare as yourself."," The section after that has the\ncommand.",[10,430,431,432,436],{},"If step 4 stops with an error, the drill has done its job early. The\ntroubleshooting notes at the foot of Supabase's guide cover the two errors\npeople hit most, both about roles, and\n",[150,433,435],{"href":434},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","how to restore a Supabase backup","\nexplains what each flag in that command protects you from.",[10,438,439,440,443,444,447,448,451,452,455,456,459,460,463,464,469],{},"Two more stops in ",[74,441,442],{},"roles.sql"," are missing from those notes. We hit both on 4\nOctober 2026, replaying the files of two Postgres 17 databases, one of them a\nSupabase project, into a fresh local copy of Supabase:\n",[74,445,446],{},"\"supabase_admin\" is a reserved role, only superusers can modify it",", on a line\nthat begins ",[74,449,450],{},"ALTER ROLE \"supabase_admin\"",", and\n",[74,453,454],{},"permission denied for parameter log_min_messages",", on a line that begins\n",[74,457,458],{},"GRANT SET ON PARAMETER",". Both lines set up roles Supabase creates in every\nproject for itself, so put ",[74,461,462],{},"--"," at the start of each line to turn it into a\ncomment and run the command again.\n",[150,465,468],{"href":466,"rel":467},"https:\u002F\u002Fgithub.com\u002FReeve-page\u002Fsupabase-backup-action",[260],"Our backup Action","\ncomments them out as it takes the copy.",[25,471,473],{"id":472},"what-to-count-and-against-what","What to count, and against what",[10,475,476],{},"Count every table in the spare and in your live project with the same query,\nand put the two lists side by side. The copy should be a night behind the live\ndatabase: a little lower on a table that grows, and never zero on a table that\nhad rows.",[10,478,479,480,482],{},"This is the head count against the list of who was in. Paste the query into\nthe SQL editor of each project and press Run. It counts the rows in every table\nof your own schema and of ",[74,481,116],{},":",[377,484,489],{"className":485,"code":487,"language":488,"meta":385},[486],"language-sql","select table_schema, table_name,\n       (xpath('\u002Frow\u002Fn\u002Ftext()',\n         query_to_xml(format('select count(*) as n from %I.%I', table_schema, table_name),\n                      false, true, '')))[1]::text::bigint as row_count\n  from information_schema.tables\n where table_schema in ('public', 'auth')\n   and table_type = 'BASE TABLE'\n order by table_schema, table_name;\n","sql",[74,490,487],{"__ignoreMap":385},[10,492,493],{},"It reads every row of every table, so on a large database run it outside your\nbusiest hours.",[10,495,496,497,500,501,482],{},"Then read the newest row of a table you know, on the spare only. Any table with\na ",[74,498,499],{},"created_at"," column will do; put its name in place of ",[74,502,503],{},"orders",[377,505,508],{"className":506,"code":507,"language":488,"meta":385},[486],"select max(created_at) from public.orders;\n",[74,509,507],{"__ignoreMap":385},[10,511,512],{},"The answer should be close to the time the backup ran. A date weeks older, or\nrows you do not recognise, means the file came from another project.",[43,514,515,528],{},[46,516,517],{},[49,518,519,522,525],{},[52,520,521],{},"Check",[52,523,524],{},"Run it on",[52,526,527],{},"What a good copy shows",[62,529,530,541,552,564],{},[49,531,532,535,538],{},[67,533,534],{},"Rows in every table",[67,536,537],{},"Both",[67,539,540],{},"The same tables, each a little behind the live count, none empty that had rows",[49,542,543,546,549],{},[67,544,545],{},"The newest row",[67,547,548],{},"The spare",[67,550,551],{},"A time from the night the copy was taken",[49,553,554,557,559],{},[67,555,556],{},"Your own account",[67,558,548],{},[67,560,561,562],{},"Your email in ",[74,563,122],{},[49,565,566,569,571],{},[67,567,568],{},"A sign-in",[67,570,548],{},[67,572,573],{},"A token comes back",[25,575,577],{"id":576},"why-the-sign-in-is-the-test-that-proves-your-accounts","Why the sign-in is the test that proves your accounts",[10,579,580,581,583],{},"A count of ",[74,582,122],{}," proves the rows arrived. A sign-in proves they work:\nthat the stored password, the spare's sign-in service and your email address\nstill agree with each other.",[10,585,586,587,589],{},"Use your own account, with a password you know. The spare's project address and\npublishable key are in its Connect panel, or in the output of ",[74,588,291],{},"\non a local copy:",[377,591,594],{"className":592,"code":593,"language":382},[380],"curl -X POST 'https:\u002F\u002F…the spare's project ref….supabase.co\u002Fauth\u002Fv1\u002Ftoken?grant_type=password' \\\n  -H \"apikey: sb_publishable_…\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"email\": \"you@example.com\", \"password\": \"…\"}'\n",[74,595,593],{"__ignoreMap":385},[10,597,598,599,604,605,608,609,612,613,616],{},"That is the sign-in call from\n",[150,600,603],{"href":601,"rel":602},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fauth\u002Fpasswords",[260],"Supabase's own documentation",",\naimed at the spare. A reply containing ",[74,606,607],{},"access_token"," means your account came\nacross with a password that works. ",[74,610,611],{},"Invalid login credentials",", for an account\nthat signs in fine on your live app, means it did not, and\n",[150,614,615],{"href":152},"why a dump arrives without the accounts"," is\nthe article for that result.",[10,618,619,620,622],{},"If everyone signs in to your app with Google, this call has nothing to test,\nbecause the spare has no Google sign-in set up. Search the restored\n",[74,621,122],{}," for your own email instead. That shows the account row came\nacross, though not that its password works.",[25,624,626],{"id":625},"files-the-half-a-database-restore-cannot-test","Files: the half a database restore cannot test",[10,628,629,630,634],{},"The database copy holds a row for every uploaded file and none of the files. If\nyou copy your Storage buckets on a job of their own, which\n",[150,631,633],{"href":632},"\u002Fblog\u002Fsupabase-storage-backup","is the only way they get copied at all",", give\nthat copy a drill too.",[10,636,637],{},"Pick the three newest rows in the restored database:",[377,639,642],{"className":640,"code":641,"language":488,"meta":385},[486],"select bucket_id, name from storage.objects order by created_at desc limit 3;\n",[74,643,641],{"__ignoreMap":385},[10,645,646],{},"Find those three paths in your copy of the files and open each one. A path with\nno file behind it is an image your users would see broken after a real\nrestore.",[25,648,650],{"id":649},"can-i-test-the-backups-supabase-takes-for-me","Can I test the backups Supabase takes for me?",[10,652,653,654,374],{},"On a paid plan, yes, with Restore to a New Project. It is the only way to open\none of Supabase's own daily copies, because on current projects\n",[150,655,657],{"href":656},"\u002Fblog\u002Fdownload-your-supabase-backup","you cannot download them",[10,659,660,661,666,667,195,669,672],{},"Supabase describes the feature as a way to\n",[150,662,665],{"href":663,"rel":664},"https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fplatform\u002Fclone-project",[260],"perform testing safely",".\nPick a copy under the Restore to a New Project tab of the Backups page, and\nSupabase builds a new project from it with your users and their hashed\npasswords, ready for the same counts and the same sign-in. Two things to know\nbefore you press it. The new project is billed as a project of its own, and\nSupabase shows you the cost before it starts. And it copies everything,\nscheduled jobs included: Supabase says jobs run by ",[74,668,337],{},[74,670,671],{},"pg_net"," and\nwrappers start running as soon as the restore completes, with no way to pause\nthem first. If a job in your app sends email or charges a card, the test\nproject will do it too.",[10,674,675],{},"If you also keep a file outside the account, that file needs a drill of its\nown.",[25,677,679],{"id":678},"how-often-should-i-test-a-restore","How often should I test a restore?",[10,681,682],{},"Once now, then every three months, and again whenever anything about how the\nbackup is taken changes.",[10,684,685],{},"The changes that matter are the ones that break a backup quietly: a new\nworkflow or an edited one, a reset database password, a move to a new plan or\na new Postgres version, a new table your app has started writing to. Each one\nis a point after which last quarter's drill no longer describes this quarter's\nfile.",[10,687,688],{},"Then write it down, which is the sheet by the door. One line per drill,\nsomewhere you can reach without the thing that broke: the date, which file, the\ncounts that mattered, whether the sign-in worked, and how long the whole drill\ntook. A text file in the backup repository will do, and so will a recurring\ncalendar entry with the results pasted into it. The date answers \"when did we\nlast prove this\" in the hour somebody needs to know, and the time it took is\nyour first estimate of how long a real restore would keep your app down.",[25,690,692],{"id":691},"where-reeve-care-fits","Where Reeve Care fits",[10,694,695],{},[17,696,697],{},"Care keeps copies of your Supabase database outside your Supabase account,\non your plan's schedule, checks every copy as it is taken, and puts it back with\na button.",[187,699,700,706,715,721,727,733],{},[190,701,702,705],{},[17,703,704],{},"Copies run on your plan's schedule",", from once a day up to every six\nhours.",[190,707,708,711,712,714],{},[17,709,710],{},"A copy that was cut off is caught as it is taken."," ",[74,713,76],{},"'s closing\nline has to be in the file, or the copy fails the check and never becomes the\ndate on your dashboard.",[190,716,717,720],{},[17,718,719],{},"Every table is counted as the copy is written."," When a table that had rows\nin the previous copy has none in this one, a person at Reeve hears about it.",[190,722,723,726],{},[17,724,725],{},"Your accounts are in the copy",", and your uploaded files come too once you\nconnect a Storage credential.",[190,728,729,732],{},[17,730,731],{},"Restoring is a button",", and a copy of the current state is taken before\nanything is replaced.",[190,734,735,738,739,195,742,195,744,746],{},[17,736,737],{},"Any copy downloads as a zip"," with ",[74,740,741],{},"schema.sql",[74,743,233],{},[74,745,442],{},"\nand a manifest holding the row count of every table, which is the \"against\nwhat\" half of this article, already written down.",[10,748,749,750,451,754,758],{},"A quarterly drill proves the copy you picked that day, and the check runs on\nevery copy as it is taken. How the copy, the check and the restore work is\ndrawn step by step on the ",[150,751,753],{"href":752},"\u002Fsupabase-backups","Supabase backups page",[150,755,757],{"href":756},"\u002Fpricing","what each plan includes",", schedule and all, is on the pricing page.",[25,760,762],{"id":761},"what-to-do-this-week","What to do this week",[764,765,766],"key-takeaways",{},[187,767,768,771,781,787,790,792],{},[190,769,770],{},"Find your newest backup and read its date. If it is older than the last scheduled run, fix the job before anything else.",[190,772,367,773,370,775,777,778,780],{},[74,774,233],{},[74,776,373],{}," and for ",[74,779,394],{},". Two searches tell you whether the file reaches its end and whether your accounts are in it.",[190,782,783,784,786],{},"Create a spare project, or start Supabase on your own computer, and replay the file with the ",[74,785,170],{}," command from Supabase's guide.",[190,788,789],{},"Run the count query on both databases and put the two lists side by side. Read the newest row of a table you know.",[190,791,427],{},[190,793,794],{},"Write down the date, the counts and how long it took, then delete the spare.",[10,796,797,798,370,800,802,803,806],{},"Before you close this tab, search your newest ",[74,799,233],{},[74,801,373],{},".\nIt is one search, and it tells you whether the file you have been calling a\nbackup reaches its own last line. If you do not have a file to search yet,\n",[150,804,805],{"href":316},"a free GitHub Action"," is the quickest way\nto start making them.",{"title":385,"searchDepth":808,"depth":808,"links":809},3,[810,812,813,814,815,816,817,818,819,820,821,822],{"id":27,"depth":811,"text":28},2,{"id":37,"depth":811,"text":38},{"id":163,"depth":811,"text":164},{"id":245,"depth":811,"text":246},{"id":349,"depth":811,"text":350},{"id":472,"depth":811,"text":473},{"id":576,"depth":811,"text":577},{"id":625,"depth":811,"text":626},{"id":649,"depth":811,"text":650},{"id":678,"depth":811,"text":679},{"id":691,"depth":811,"text":692},{"id":761,"depth":811,"text":762},"Backups","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcover-1200x630.png","A row of identical sealed backup files, the front one lit, beside an empty dashed database waiting for one of them to be opened into 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.",false,"md",[830,832,834,837,840,843],{"q":28,"a":831},"Restore it into a project that holds nothing you care about and look at what comes back. Compare the row count of every table with the live database, check that the newest row is from the night the copy was taken, and sign in as yourself. Nothing about the file itself tells a good dump from a broken one: its name, its size and the green tick beside the job are the same either way.",{"q":679,"a":833},"Once now, then every three months, and again whenever the way the backup is taken changes: a new or edited workflow, a reset database password, a new plan or Postgres version, or a new table your app writes to. Write down the date each time, with the counts and how long it took, so the question of when it was last proved has an answer.",{"q":835,"a":836},"Can I test a restore without touching my live database?","Yes, and you always should. Restore into a spare Supabase project, which the free plan allows, or into Supabase running on your own computer through the CLI and Docker. The only thing the drill does to the live database is read it once, to count its rows. Delete the spare afterwards, because it holds a real copy of your users.",{"q":838,"a":839},"What should I check after restoring a backup?","Four things. The row count of every table next to the live one, where the copy should be a little behind and never at zero for a table that had rows. The newest row in a table you know, which should be from the night of the copy. Your own email in auth.users. And a sign-in as yourself, which is the only check that proves the accounts work rather than merely arrived.",{"q":841,"a":842},"My restore worked but nobody can log in. Why?","Most often because the file never held your accounts. Supabase keeps them in a schema called auth, and a dump that named only your own schema, or a bare supabase db dump, leaves them out while every table of yours restores cleanly. Search the file for COPY \"auth\".\"users\" before anything else. If it is missing, take a data dump with --data-only, which walks the auth schema and brings your users with it.",{"q":164,"a":844},"Not always. We cut a pg_dump file at three points and replayed each with --single-transaction and ON_ERROR_STOP, the flags Supabase uses in its restore guide. A cut in the middle of a value stopped with an error. A cut inside the last column of a row and a cut between two tables both finished without one, with the tables short. A complete dump carries the line PostgreSQL database dump complete near its end, so search for it before you trust a file.","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",[847,848,849,850,851,852,853,854],"test supabase backup","verify supabase backup","supabase restore test","how to test a database backup","restore backup to test database","is my supabase backup working","supabase backup verification","restore drill",{},true,"Test your Supabase backup before you need it","\u002Fblog\u002Ftest-your-supabase-backup","2026-09-28",{"title":5,"description":826},"blog\u002Ftest-your-supabase-backup",[863,864,865],"To test a Supabase backup, restore it into a project that holds nothing you care about, compare its row counts with the live database, and sign in as yourself. Nothing short of a restore tells a good file from a broken one.","We cut a dump off partway through a table and replayed it with the flags Supabase recommends. psql finished without an error, and the tables came back short.","Run the drill once now, again every three months and after any change to how the backup is taken, and write down the date each time.","2026-10-05","FET63w2Roe4wh0UMax6lIQR4jL4r4378pd0SKstHku4",[869,876,882,888,894,900,906,911,917,923,929,935,941,947,948,953,958,963,969,975,980,986,992,998,1004,1010,1016,1022,1028,1033,1039,1045,1051,1057,1063,1069,1075,1081,1087,1093,1099,1105,1111,1117,1123,1129,1135,1141,1147,1153,1158,1164,1170,1176,1182,1187,1193,1199,1205,1210,1216],{"path":870,"title":871,"description":872,"published":873,"category":874,"image":875,"draft":827},"\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","Security basics","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":877,"title":878,"description":879,"published":880,"category":874,"image":881,"draft":827},"\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":883,"title":884,"description":885,"published":886,"category":874,"image":887,"draft":827},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Your API key leaked. Here is the order to do things in","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.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":889,"title":890,"description":891,"published":892,"category":874,"image":893,"draft":827},"\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":895,"title":896,"description":897,"published":898,"category":874,"image":899,"draft":827},"\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":901,"title":902,"description":903,"published":904,"category":874,"image":905,"draft":827},"\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":907,"title":908,"description":909,"published":866,"category":874,"image":910,"draft":827},"\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.","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":912,"title":913,"description":914,"published":915,"category":874,"image":916,"draft":827},"\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":918,"title":919,"description":920,"published":921,"category":874,"image":922,"draft":827},"\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":924,"title":925,"description":926,"published":927,"category":874,"image":928,"draft":827},"\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":930,"title":931,"description":932,"published":933,"category":874,"image":934,"draft":827},"\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":936,"title":937,"description":938,"published":939,"category":874,"image":940,"draft":827},"\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":942,"title":943,"description":944,"published":945,"category":874,"image":946,"draft":827},"\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":858,"title":5,"description":826,"published":859,"category":823,"image":845,"draft":827},{"path":316,"title":949,"description":950,"published":951,"category":823,"image":952,"draft":827},"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":656,"title":954,"description":955,"published":956,"category":823,"image":957,"draft":827},"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":959,"title":960,"description":961,"published":956,"category":874,"image":962,"draft":827},"\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":964,"title":965,"description":966,"published":967,"category":874,"image":968,"draft":827},"\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":970,"title":971,"description":972,"published":973,"category":874,"image":974,"draft":827},"\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":152,"title":976,"description":977,"published":978,"category":823,"image":979,"draft":827},"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":981,"title":982,"description":983,"published":984,"category":874,"image":985,"draft":827},"\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":987,"title":988,"description":989,"published":990,"category":874,"image":991,"draft":827},"\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":993,"title":994,"description":995,"published":996,"category":874,"image":997,"draft":827},"\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":999,"title":1000,"description":1001,"published":1002,"category":874,"image":1003,"draft":827},"\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":1005,"title":1006,"description":1007,"published":1008,"category":874,"image":1009,"draft":827},"\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":1011,"title":1012,"description":1013,"published":1014,"category":874,"image":1015,"draft":827},"\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":1017,"title":1018,"description":1019,"published":1020,"category":874,"image":1021,"draft":827},"\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":1023,"title":1024,"description":1025,"published":1026,"category":874,"image":1027,"draft":827},"\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":632,"title":1029,"description":1030,"published":1031,"category":823,"image":1032,"draft":827},"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":1034,"title":1035,"description":1036,"published":1037,"category":823,"image":1038,"draft":827},"\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":1040,"title":1041,"description":1042,"published":1043,"category":823,"image":1044,"draft":827},"\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":1046,"title":1047,"description":1048,"published":1049,"category":874,"image":1050,"draft":827},"\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":1052,"title":1053,"description":1054,"published":1055,"category":874,"image":1056,"draft":827},"\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":1058,"title":1059,"description":1060,"published":1061,"category":874,"image":1062,"draft":827},"\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":1064,"title":1065,"description":1066,"published":1067,"category":874,"image":1068,"draft":827},"\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":1070,"title":1071,"description":1072,"published":1073,"category":874,"image":1074,"draft":827},"\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":1076,"title":1077,"description":1078,"published":1079,"category":874,"image":1080,"draft":827},"\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":1082,"title":1083,"description":1084,"published":1085,"category":874,"image":1086,"draft":827},"\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":1088,"title":1089,"description":1090,"published":1091,"category":874,"image":1092,"draft":827},"\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":1094,"title":1095,"description":1096,"published":1097,"category":874,"image":1098,"draft":827},"\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":1100,"title":1101,"description":1102,"published":1103,"category":874,"image":1104,"draft":827},"\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":1106,"title":1107,"description":1108,"published":1109,"category":823,"image":1110,"draft":827},"\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":1112,"title":1113,"description":1114,"published":1115,"category":874,"image":1116,"draft":827},"\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":1118,"title":1119,"description":1120,"published":1121,"category":823,"image":1122,"draft":827},"\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":1124,"title":1125,"description":1126,"published":1127,"category":874,"image":1128,"draft":827},"\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":1130,"title":1131,"description":1132,"published":1133,"category":874,"image":1134,"draft":827},"\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":1136,"title":1137,"description":1138,"published":1139,"category":874,"image":1140,"draft":827},"\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":1142,"title":1143,"description":1144,"published":1145,"category":874,"image":1146,"draft":827},"\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":1148,"title":1149,"description":1150,"published":1151,"category":823,"image":1152,"draft":827},"\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":434,"title":1154,"description":1155,"published":1156,"category":823,"image":1157,"draft":827},"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":1159,"title":1160,"description":1161,"published":1162,"category":874,"image":1163,"draft":827},"\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":1165,"title":1166,"description":1167,"published":1168,"category":874,"image":1169,"draft":827},"\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":1171,"title":1172,"description":1173,"published":1174,"category":823,"image":1175,"draft":827},"\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":1177,"title":1178,"description":1179,"published":1180,"category":874,"image":1181,"draft":827},"\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":1183,"title":1184,"description":1185,"published":1180,"category":874,"image":1186,"draft":827},"\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":1188,"title":1189,"description":1190,"published":1191,"category":874,"image":1192,"draft":827},"\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":1194,"title":1195,"description":1196,"published":1197,"category":823,"image":1198,"draft":827},"\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":1200,"title":1201,"description":1202,"published":1203,"category":874,"image":1204,"draft":827},"\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":1206,"title":1207,"description":1208,"published":1203,"category":823,"image":1209,"draft":827},"\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":1211,"title":1212,"description":1213,"published":1214,"category":823,"image":1215,"draft":827},"\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":1217,"title":1218,"description":1219,"published":1214,"category":874,"image":1220,"draft":827},"\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",1791705957970]