# Can anyone upload files to your app at your cost?

Checked on 1 November 2026

Yes, if one of your app’s storage rules lets visitors who are not signed in add files: a stranger can then fill your storage at your cost, and if the bucket is public, anyone can download those files on your bill too.

To close it, allow uploads only for signed-in users and only into their own folder, give every bucket a file size limit and a list of allowed file types, and keep buckets private unless their files are meant for everyone.

To check, read your storage rules in Lovable or in the Supabase dashboard. LidTight’s free check counts the files a stranger can list, and its deep check tries one small upload as a stranger.

## How file storage works in apps built with AI

Lovable’s built-in backend, Cloud, is [built on Supabase](https://docs.lovable.dev/features/cloud), storage included. When you ask for uploads, such as a profile photo or a PDF, [Lovable creates the buckets and the upload flow](https://docs.lovable.dev/features/storage). A bucket is a named container for files, with its own settings.

Two separate settings decide what people can do with a bucket:

- **Public or private** decides who can read the files. In a public bucket, [anyone who has a file’s address can open it](https://supabase.com/docs/guides/storage/buckets/fundamentals). In a private bucket, every read, downloads included, goes through your access rules. Buckets are private by default.
- **Storage access rules** decide who can upload, change and delete files. Supabase calls them row level security (RLS) policies, and they sit on a table named storage.objects. [Without a rule, Supabase allows no uploads at all](https://supabase.com/docs/guides/storage/security/access-control).

So public or private does not decide who can upload: Supabase’s docs say [public buckets still enforce access control](https://supabase.com/docs/guides/storage/buckets/fundamentals) for uploads. Trouble starts when an upload rule lets in more people than you meant.

Your app’s public code holds your Supabase address and its [publishable key](https://supabase.com/docs/guides/getting-started/api-keys). A visitor who is not signed in uses the anon role; a signed-in user uses the authenticated role. A rule with no “to authenticated” line [applies to all roles](https://www.postgresql.org/docs/current/sql-createpolicy.html), anon included. If its check also lets a stranger through, such as “true” or a bucket name only, anyone can upload straight to your storage, so hiding the upload button changes nothing. See [why a visible public key is fine and an open rule is not](https://lidtight.com/guides/supabase-public-key).

## What an open bucket costs you

An upload rule open to strangers costs you in three ways. The prices are Supabase’s on 1 November 2026.

- **Storage.** Supabase [charges for the total size of all files in your buckets](https://supabase.com/docs/guides/platform/manage-your-usage/storage-size). The Free plan includes 1 GB. The Pro plan includes 100 GB, then charges $0.0213 per GB per month. On a paid plan with the spend cap off, or on the Team plan and above, you pay any overage. On the Free plan, or with the spend cap on, you get a notice and a grace period.
- **Downloads.** Each file sent out of storage counts as [egress](https://supabase.com/docs/guides/platform/manage-your-usage/egress), the data Supabase sends to visitors. The Free plan includes 5 GB uncached and 5 GB cached. The Pro plan includes 250 GB of each, then charges $0.09 per GB uncached and $0.03 per GB cached. In a public bucket, every download of a stranger’s file counts.
- **Their files at your address.** A file in a public bucket has a public address, and in Supabase that address [holds your project ID](https://supabase.com/docs/guides/storage/serving/downloads). A stranger can store files you never approved and share the links from your project’s address.

A private bucket with an open upload rule still fills up, but strangers cannot read the files back unless a read rule lets them. Limits matter even after you close the rule: if [sign-up needs no email confirmation](https://lidtight.com/guides/signup-without-email-confirmation), a bot can sign up and upload as a “signed-in user”.

## How to check your own app

These steps need access to your own project. They only read settings and change nothing.

1. List your buckets. In Lovable, open **More → Storage**: each bucket card says [Private or Public](https://docs.lovable.dev/features/storage). In Supabase, open the Storage page of the dashboard. Note who should upload to each bucket and who should read it.
2. Read the storage rules. In Lovable, open **More → Database → Manage → Policies**, where you can [review every policy](https://docs.lovable.dev/features/security). In Supabase, run the first query below in the SQL Editor. It reads Postgres’s [list of policies](https://www.postgresql.org/docs/current/view-pg-policies.html).
3. Check each rule for INSERT (upload) or ALL. It is open to strangers if its roles show public or anon and its with_check lets anyone pass, such as true or a bucket name only. It is too wide if it does not limit the bucket and the user’s own folder. A WITH CHECK (true) rule, in [Supabase’s words](https://supabase.com/docs/guides/observability/advisors), “allows writing any row”.
4. Run the second query. A bucket with no size limit and no allowed types of its own accepts any file up to your project’s [global size limit](https://supabase.com/docs/guides/storage/uploads/file-limits).
5. Run the third query, based on [Supabase’s usage docs](https://supabase.com/docs/guides/platform/manage-your-usage/storage-size), to see the size of each bucket. Look inside any bucket that is larger than you expect.
6. Run the scans you already have. Lovable’s Quick scan runs each time you publish and [covers file storage rules](https://docs.lovable.dev/features/security); its Deep scan looks for routes that anyone can upload to. Supabase’s [Security Advisor](https://supabase.com/docs/guides/observability/advisors) warns when a public bucket lets clients list its files.

```
-- 1. Who may upload, read, change or delete stored files
select policyname, roles, cmd, qual, with_check
from pg_policies
where schemaname = 'storage' and tablename = 'objects';

-- 2. Each bucket: public or private, size limit, allowed types
select id, public, file_size_limit, allowed_mime_types
from storage.buckets;

-- 3. Size of each bucket (Supabase's example, with bigint so files over 2 GB do not break it)
select bucket_id,
  (sum((metadata->>'size')::bigint) / 1048576.0)::numeric(10, 2) as total_size_megabyte
from storage.objects
group by bucket_id
order by total_size_megabyte desc;
```

## How to fix it

Apply these steps yourself or give them to your AI builder. Test real uploads after each change, because a rule that is too tight breaks them.

1. Keep each bucket private unless its files are meant for everyone, such as profile pictures or blog images ([Supabase](https://supabase.com/docs/guides/storage/buckets/fundamentals)). Lovable [blocks public buckets by default](https://docs.lovable.dev/features/storage). Show private files through signed URLs, which work only for a limited time.
2. Remove every upload rule that lets in the public or anon role, or that checks only “true”.
3. Add one upload rule per bucket: signed-in users only, and only into a folder named with their own user ID. The code below is [Supabase’s example](https://supabase.com/docs/guides/storage/security/access-control). Put your bucket’s name in place of my_bucket_id, and make sure your app saves each file under the user’s ID.
4. Set a file size limit on each bucket. In Supabase, choose **Edit bucket** in the bucket’s menu and turn on [Restrict file size](https://supabase.com/kb/troubleshooting/upload-file-size-restrictions-Y4wQLT). In Lovable, the **Upload size limit** under **More → Storage** is [2 GB by default](https://docs.lovable.dev/features/storage) and applies to the whole app. Choose the largest file your app really needs.
5. Set the allowed file types on each bucket, such as image/* for photos. Supabase [rejects any upload that breaks a bucket’s type or size limit](https://supabase.com/docs/guides/storage/buckets/creating-buckets).
6. Delete files you do not recognize from the Storage page or through the API, not with SQL. Deleting the row in SQL [leaves the file in place, and you are still billed for it](https://supabase.com/docs/guides/storage/schema/design).
7. Keep the secret key (sb_secret_…, or the older service_role key) out of browser code. It [skips every access rule](https://supabase.com/docs/guides/getting-started/api-keys), storage rules included.

```
-- Remove an open upload rule (use its name from query 1)
drop policy if exists "name of the open rule" on storage.objects;

-- Signed-in users upload only into a folder named with their user ID
create policy "Allow authenticated uploads"
on storage.objects
for insert
to authenticated
with check (
  bucket_id = 'my_bucket_id' and
  (storage.foldername(name))[1] = (select auth.jwt()->>'sub')
);
```

> Check again after any change to uploads, such as a new bucket or a new upload form. The [launch checklist](https://lidtight.com/guides/launch-security-checklist) covers the other settings to check before you go live.

## What LidTight’s check shows, and what it cannot see

LidTight tests from the outside, as a stranger, so it shows what your rules allow in practice. The [free check](https://lidtight.com/#check-now) finds the buckets named in the code any visitor downloads, and counts the files a stranger can list in each. It keeps the count, not the file names.

The deep checks run only when the app’s owner ticks the box for them under the app link. For each bucket named in the code, the upload check sends one small text file with only your app’s public key and no sign-in, then at once asks to delete it. An open bucket gets the result “Anyone can upload files to your storage, at your cost”, and the report names the bucket.

The test file’s name starts with lidtight-test- and it holds the words “LidTight security test”. The check does not confirm that the delete worked. If your rules let strangers upload but not delete, the file stays, so look for it on the Storage page and delete it there.

The check cannot see:

- Buckets that your public code does not name, such as a bucket used only by server code.
- Rules for signed-in users. The upload check runs as a stranger only, so it does not show whether one user can write into another user’s folder.
- How large a file a bucket accepts. The test file is small.
- An open upload rule on a bucket that accepts only some file types, such as images. The test file is plain text, so the bucket refuses it whatever the rule says.
- Apps on Base44 or Firebase. The upload check works with Supabase and Lovable Cloud only.

For apps on Supabase or Lovable Cloud, the queries above show all of these except the last one.

## Sources

1. [Supabase: Storage access control](https://supabase.com/docs/guides/storage/security/access-control)
2. [Supabase: Storage buckets (public and private)](https://supabase.com/docs/guides/storage/buckets/fundamentals)
3. [Supabase: Creating buckets (restricting uploads)](https://supabase.com/docs/guides/storage/buckets/creating-buckets)
4. [Supabase: Storage upload limits](https://supabase.com/docs/guides/storage/uploads/file-limits)
5. [Supabase: Upload file size restrictions](https://supabase.com/kb/troubleshooting/upload-file-size-restrictions-Y4wQLT)
6. [Supabase: The storage schema](https://supabase.com/docs/guides/storage/schema/design)
7. [Supabase: Serving assets from Storage](https://supabase.com/docs/guides/storage/serving/downloads)
8. [Supabase: Manage Storage size usage](https://supabase.com/docs/guides/platform/manage-your-usage/storage-size)
9. [Supabase: Manage Egress usage](https://supabase.com/docs/guides/platform/manage-your-usage/egress)
10. [Supabase: API keys](https://supabase.com/docs/guides/getting-started/api-keys)
11. [Supabase: Advisors](https://supabase.com/docs/guides/observability/advisors)
12. [Lovable: Cloud, the built-in backend](https://docs.lovable.dev/features/cloud)
13. [Lovable: Storage](https://docs.lovable.dev/features/storage)
14. [Lovable: Security](https://docs.lovable.dev/features/security)
15. [PostgreSQL: CREATE POLICY](https://www.postgresql.org/docs/current/sql-createpolicy.html)
16. [PostgreSQL: pg_policies](https://www.postgresql.org/docs/current/view-pg-policies.html)
