[{"data":1,"prerenderedAt":350},["ShallowReactive",2],{"blog-en-supabase-storage-bucket-public":3},{"id":4,"title":5,"body":6,"category":308,"cover":309,"coverAlt":310,"description":311,"draft":312,"extension":313,"faq":314,"image":330,"keywords":331,"meta":338,"navigation":339,"ogTitle":340,"path":341,"published":342,"seo":343,"stem":344,"tldr":345,"updated":342,"__hash__":349},"blog_en\u002Fblog\u002Fsupabase-storage-bucket-public.md","Your Supabase storage bucket is public. Is that a problem?",{"type":7,"value":8,"toc":297},"minimark",[9,13,16,24,29,32,35,38,41,45,48,110,113,116,122,126,129,141,151,154,157,171,175,178,193,196,201,204,211,215,218,224,233,236,240,243,246,249,252,256,284],[10,11,12],"p",{},"Someone opened your app, spent a minute poking at it, and sent you a message:\nyour Supabase storage bucket is public, and anyone can list what is in it.",[10,14,15],{},"That is two separate claims. One of them is probably a setting you chose on\npurpose and should keep. The other is the one worth an afternoon.",[10,17,18,19,23],{},"Here is the part that guide after guide gets wrong: ",[20,21,22],"strong",{},"public and listable are\ntwo different switches, and the alarming one is not the switch everybody tells\nyou to flip."," Making a bucket private closes the first. It can leave the second\nstanding wide open, and a bucket that has never been public in its life can be\nlistable this afternoon.",[25,26,28],"h2",{"id":27},"is-a-public-supabase-storage-bucket-a-security-problem","Is a public Supabase storage bucket a security problem?",[10,30,31],{},"On its own, no. It means one specific thing, and that thing is very often what\nyou wanted.",[10,33,34],{},"Marking a bucket public gives every file in it a URL that works without a login.\nThat is the whole feature. It is how the avatar on a comment loads for a reader\nwho has no account, how your logo appears in an email, how a product photo shows\nup for someone who is still deciding whether to sign up.",[10,36,37],{},"A public bucket is closer to an unlisted phone number than to an unlocked door.\nThe line connects for anyone who dials it, and there is no directory to look the\nnumber up in. Whether that is fine depends on one question, which is how easily\nsomebody could arrive at the number without being told it.",[10,39,40],{},"Supabase makes new buckets private and puts a warning on the toggle, so a public\nbucket is something somebody switched on: you, or your builder, getting an\nupload feature to work.",[25,42,44],{"id":43},"what-the-public-toggle-actually-turns-on","What the public toggle actually turns on",[10,46,47],{},"One row of the table below, and nothing else on it.",[49,50,51,67],"table",{},[52,53,54],"thead",{},[55,56,57,61,64],"tr",{},[58,59,60],"th",{},"What someone tries",[58,62,63],{},"Public bucket",[58,65,66],{},"Private bucket",[68,69,70,82,92,101],"tbody",{},[55,71,72,76,79],{},[73,74,75],"td",{},"Opening a file whose exact URL they have",[73,77,78],{},"Works, no login needed",[73,80,81],{},"Needs a policy or a signed link",[55,83,84,87,90],{},[73,85,86],{},"Asking for a list of the bucket contents",[73,88,89],{},"Only if a policy allows it",[73,91,89],{},[55,93,94,97,99],{},[73,95,96],{},"Uploading a file",[73,98,89],{},[73,100,89],{},[55,102,103,106,108],{},[73,104,105],{},"Deleting or replacing a file",[73,107,89],{},[73,109,89],{},[10,111,112],{},"Supabase's own troubleshooting page states it about as plainly as it can be\nstated: a public bucket means there is a public URL you can use to download the\nfile, and every other operation still has to satisfy the policies on that\nbucket.",[10,114,115],{},"So the public toggle is a smaller control than its name suggests. Three of those\nfour rows are decided somewhere else entirely, under Storage → Policies, and the\nmessage you received was almost certainly about the second one.",[117,118],"diagram",{"alt":119,"caption":120,"src":121},"Four buckets side by side. Each has a public toggle, either off or on, and a policy box that is either empty or filled. The two buckets whose policy box holds a condition naming only the bucket show all six of their files lit and crossed, whichever way the toggle is set. The two with an empty or narrow policy show nothing listed and are ticked.","The toggle across the top changes one thing. The box underneath it decides whether a stranger gets the list, and it does that the same way whether the bucket is public or private.","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fpublic-and-listable-1600x760.png",[25,123,125],{"id":124},"why-a-private-bucket-can-still-be-listable","Why a private bucket can still be listable",[10,127,128],{},"Because listing is granted by an access policy, and the policy most people paste\nin grants it to everybody.",[10,130,131,132,136,137,140],{},"Every read from Storage passes through Row Level Security on a table called\n",[133,134,135],"code",{},"storage.objects",". Listing a bucket is a read, filed under ",[133,138,139],{},"SELECT",", exactly\nlike downloading a file is. Supabase's own quickstart shows a policy in this\nshape, which is where copies of it tend to come from:",[142,143,149],"pre",{"className":144,"code":146,"language":147,"meta":148},[145],"language-sql","create policy \"Public Access\"\n  on storage.objects for select\n  using ( bucket_id = 'public' );\n","sql","",[133,150,146],{"__ignoreMap":148},[10,152,153],{},"Read what that says. Any request at all may read anything in that bucket. It\ndoes not ask who is asking, and it does not ask whether the file has anything to\ndo with them. Reading covers downloading a file, and it covers handing over the\nlist of files, because those are the same permission wearing two hats.",[10,155,156],{},"The bucket's own public toggle never comes into it. A policy like that on a\nprivate bucket makes the bucket listable, and the dashboard will go on\ntruthfully describing it as private.",[10,158,159,160,165,166,170],{},"The word doing the damage is \"any\". A request carrying the publishable key that\nships inside your app's code satisfies that policy perfectly, and\n",[161,162,164],"a",{"href":163},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","that key is meant to be readable by everyone",",\nwhich is precisely why the policy is supposed to be the part doing the work.\nThis is the same shape as the problem next door, where\n",[161,167,169],{"href":168},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Row Level Security is switched on and permitting everything anyway",".",[25,172,174],{"id":173},"what-someone-gets-from-a-list-of-your-files","What someone gets from a list of your files",[10,176,177],{},"Names, mostly. Which is a great deal more than it sounds.",[10,179,180,181,184,185,188,189,192],{},"Filenames tend to describe their contents, because a person chose them while\nthinking about what was inside: ",[133,182,183],{},"invoice-march-acme.pdf",", ",[133,186,187],{},"passport-front.jpg",",\n",[133,190,191],{},"payroll-final-v2.xlsx",". A list of those is a fair summary of your business, and\na count of them says roughly how many customers you have. If the bucket is also\npublic, every name on that list is a working link.",[10,194,195],{},"Back to the phone number. Being unlisted is worth something right up until the\ndirectory gets published, and afterwards it never mattered how hard the number\nwas to guess.",[117,197],{"alt":198,"caption":199,"src":200},"Two halves. On the left, a request to the public object path reaches one file out of eight; the other seven are drawn as dashed outlines the request never finds. On the right, a request to the list path returns all eight as solid rows.","Without listing a stranger has to arrive at a filename somehow. With it, they are handed every filename you have.","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fguess-or-list-1600x700.png",[10,202,203],{},"The way this surfaces is ordinary. A document belonging to one customer turns up\nin front of another one. Somebody quotes the name of a file back to you that\nthey had no way of knowing. A folder of uploads gets copied wholesale by an\nautomated crawler that was reading every Supabase project it could find.",[10,205,206,207,170],{},"Our free scan asks your Storage for a listing using nothing except the key that\nis already in your app's code, and tells you which buckets answered with\ncontents. It reads names and never downloads a file. It takes about 20 seconds\nand needs no account: ",[161,208,210],{"href":209},"\u002F#scan","scan your app",[25,212,214],{"id":213},"how-to-tell-which-one-you-have","How to tell which one you have",[10,216,217],{},"Two checks in the Supabase dashboard, and the second is the one the message was\nreally about.",[10,219,220,223],{},[20,221,222],{},"Storage → Buckets."," The public ones are labelled as public. For each one, ask\nwhether every single file in it is something you would be content to show a\nstranger. Not most files. Every file, including whatever gets uploaded there\nnext week by a feature you have not built yet.",[10,225,226,229,230,232],{},[20,227,228],{},"Storage → Policies."," Read each ",[133,231,139],{}," policy that touches the bucket. A\ncondition that mentions only the bucket name lets anyone read it. A condition\ncomparing the file's owner to the person making the request is doing real work.\nAn empty policy list on a private bucket means nothing gets read at all, which\nis restrictive and safe.",[10,234,235],{},"If your app has to show a private file to the right person, the tool for that is\na signed link: your server asks Supabase for a URL that works for a set number\nof minutes and then stops working. That keeps the file private and still gets it\nonto the page.",[25,237,239],{"id":238},"when-public-is-the-right-answer","When public is the right answer",[10,241,242],{},"More often than a security article usually admits, and it is worth saying so.",[10,244,245],{},"If the file is meant for everyone, public is correct, and working around it buys\nyou a slower app and more code to maintain. Avatars, logos, cover images,\nanything a signed-out visitor is supposed to see: put them in a public bucket\nand stop thinking about it.",[10,247,248],{},"The one thing to do even there is keep the listing off. A public bucket of\navatars is a feature. The same bucket handing over the complete list of everyone\nwho has ever uploaded one is a different thing that you did not ask for.",[10,250,251],{},"If the file belongs to one particular person, it goes in a private bucket with a\npolicy that checks who is asking, and reaches the page through a signed link.\nThe test is a single question: who is this file for? Everyone, or one named\naccount.",[25,253,255],{"id":254},"what-to-do-this-week","What to do this week",[257,258,259],"key-takeaways",{},[260,261,262,266,275,278,281],"ul",{},[263,264,265],"li",{},"Open Storage → Buckets and write down which ones are public. For each, decide whether it holds files meant for everyone or files meant for one person.",[263,267,268,269,271,272,274],{},"Read every ",[133,270,139],{}," policy on ",[133,273,135],{},". A condition that names only the bucket grants the file list to anyone; that is the one that made the message you received true.",[263,276,277],{},"Move anything belonging to a single user into a private bucket and serve it through signed links, which expire on their own.",[263,279,280],{},"Stop naming uploaded files after their contents. A random identifier costs nothing and means a leaked list gives up much less than it would have.",[263,282,283],{},"Back up your Storage separately from your database. Your uploads are in neither your code nor your Postgres tables, so nothing that copies those two is copying your files.",[10,285,286,287,291,292,296],{},"Open Storage → Policies in your project and read what is in there, before you\nchange a single toggle. That one screen is what decides whether the second half\nof the message you got was true. The\n",[161,288,290],{"href":289},"\u002Fchecklist","10-minute security checklist"," covers it next to the rest of what a\nnewly launched app tends to leave open, and if a bucket did turn out to be\nreadable, ",[161,293,295],{"href":294},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","what a stranger can reach in your database","\nis the next thing to read, since the policy that hands over a file list is the\nsame shape as the one that hands over a table.",{"title":148,"searchDepth":298,"depth":298,"links":299},3,[300,302,303,304,305,306,307],{"id":27,"depth":301,"text":28},2,{"id":43,"depth":301,"text":44},{"id":124,"depth":301,"text":125},{"id":173,"depth":301,"text":174},{"id":213,"depth":301,"text":214},{"id":238,"depth":301,"text":239},{"id":254,"depth":301,"text":255},"Security basics","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcover-1200x630.png","Documents inside a container. A bar covers most of the opening and stops short, leaving four of them standing in the gap.","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.",false,"md",[315,318,321,324,327],{"q":316,"a":317},"Someone told me my Supabase storage bucket is public. Should I make it private?","Not until you know what is in it. Public buckets exist for a reason: profile pictures, logos, product images and anything else your app shows to a visitor who has not signed in. For those files public is the correct setting. For invoices, ID documents, exports or anything belonging to one particular user, it is the wrong one, and private plus signed links is what you want instead. The question was never whether public is bad. It is whether those particular files were meant to be seen by anyone who asks.",{"q":319,"a":320},"What is the difference between a public bucket and a listable one?","Public decides whether a file opens for someone who already has its URL. Listable decides whether a stranger can ask your project for the entire contents of a bucket and get an answer. They are configured in different places in the Supabase dashboard, and the second one is what turns a guess into a directory.",{"q":322,"a":323},"Can someone guess the URLs of files in my public bucket?","That depends entirely on how your app names them. If it uploads under the original filename, or under something orderly like invoice-4.pdf, then yes, and it takes very little effort. If it uploads under a long random identifier, guessing is impractical. Which is exactly why listing matters so much: it removes the guessing step completely.",{"q":325,"a":326},"How do I stop people listing my bucket?","Listing goes through a Row Level Security policy on the storage.objects table, so that is where it gets fixed rather than on the bucket itself. Open Storage → Policies in your Supabase dashboard and read every SELECT policy that touches the bucket. One whose only condition is the bucket name lets any request read that bucket, its file list included. Narrow it so it compares the file to the person asking for it, or remove it and hand out signed links from your server instead.",{"q":328,"a":329},"Are my uploaded files covered by my database backup?","No. Storage sits outside your Postgres database, so a database backup holds the rows that point at your files and none of the files themselves. Restoring it gives you a table full of links to things that are no longer there. Your uploads have to be copied separately, and most people find this out on the day it matters.","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",[332,333,334,335,336,337],"supabase storage bucket public","supabase public bucket security","supabase storage access policy","list files in supabase bucket","make supabase bucket private","supabase signed url",{},true,"Is a public Supabase storage bucket a problem?","\u002Fblog\u002Fsupabase-storage-bucket-public","2026-08-23",{"title":5,"description":311},"blog\u002Fsupabase-storage-bucket-public",[346,347,348],"A public Supabase storage bucket means one thing: anyone holding a file URL can open it without logging in. It says nothing about whether they can see what else is in there.","Listing is granted by an access policy instead of by the public toggle, so a private bucket can be listable while a public one is not.","Listing is the one to fix today, because it saves a stranger the trouble of guessing a single filename.","I7-2gU8Chm2dzX8FLP_Tl8l5C8KkLWnDUkuHunvhXA4",1787826048204]