Question

How do I let users upload a profile picture without exposing my bucket?

Solved · 163 viewsasked by kai_makes

I want avatars. ChatGPT gave me code that makes the bucket public and uploads from the browser with the service key. That feels wrong but I don't know the right way.

What I’ve tried

Made the bucket public, it works, but now anyone can upload anything.

Comment

2 answers

Marked as helpful by the asker
lena_ops

Your instinct is right. Never ship the service key to a browser, it bypasses every rule you have.

The standard setup:

  1. Bucket public for reading (avatars are public anyway), private for writing.
  2. A storage policy that lets a user write only into their own folder:
create policy "own avatar" on storage.objects for insert
  with check (bucket_id = 'avatars' and (storage.foldername(name))[1] = auth.uid()::text);
  1. Upload from the browser with the anon key and the user's session. Limit file size and type in the same policy or in the client.

Delete the service key from your frontend code today and rotate it in the dashboard, it has been in your bundle.

Comment
Service key is out of the frontend and rotated. Upload works with the policy. But the second upload for the same user gives "The resource already exists". kai_makes
Upload with upsert: true. Upsert is an update when the file exists, so you need an update policy with the same folder check as the insert one. lena_ops
Added the update policy and upsert. Replacing the avatar works now. Thanks! kai_makes
amir_h

Two things to add to the policy setup.

Put the size and type limits on the bucket itself (Storage → bucket settings → allowed MIME types image/png, image/jpeg, image/webp, max 2 MB). That is enforced by Storage, so it holds even when someone calls the API directly. The checks in your upload form are for UX only.

And upload to a fixed name per user, ${user.id}/avatar.webp, instead of the original filename. Then every user has exactly one object, old avatars don't pile up, and you never serve a file called IMG_4411 (2).HEIC.

Comment
Fixed name is smart, I had four copies of my own face in the bucket already. kai_makes