Vibe Coding Uploads

Adding File Uploads to Your Vibe Coded App

Your app has users now, and the next request is almost always the same shape: let people upload a profile photo, a document, a receipt, an attachment. It looks like a small feature — a file picker and an "Upload" button — but it fails differently from everything else you've added. A broken button is annoying; an upload feature done carelessly quietly grows a storage bill forever, serves a "private" file to anyone with the link, or stores a file that isn't what its name claims to be. This guide covers the safe way to add uploads as a vibe coder: why the file shouldn't land on your own server, the exact prompt to ask for, and why a link that's merely unlisted is not the same as a file that's actually protected.

Last reviewed: Jul 20 2026

A glowing file icon moving along a track through a corridor of layered checkpoint gates toward a storage vault, with one gate lit in warm amber where the file is scanned before it's allowed to pass.
A locked gate stops the wrong file — an unlisted door only slows down whoever finds it.

Why Uploads Deserve Extra Scrutiny

A file upload is one of the easiest features to ask AI for, and one of the easiest to get wrong in ways that don't show up until later. AI can produce a working file picker, an "Upload successful" message, and a photo that displays back on the page — and it will look finished. What's easy to miss: whether the file went somewhere that scales, whether "private" actually means private, and whether the app checked what the file really is instead of trusting what its name or extension claims.

The good news is the same as with accounts and payments: you don't need to understand storage infrastructure to do this safely. You need to avoid one common mistake — writing uploaded files to your own server or database — and confirm two things AI tools often skip by default: that access to a file is enforced by the storage provider, not by hiding its URL, and that the app checks what was actually uploaded rather than what the browser claimed to send.

Never Store Uploaded Files as Blobs in Your App Database

If an AI tool generates code that saves an uploaded file's raw bytes directly into your application database, or writes it to a folder on the same server that runs your app, stop. That approach doesn't scale, is awkward to back up, and usually skips validation entirely. The fix below sends the file straight to a dedicated storage service instead, the same way hosted checkout keeps card numbers out of your own database.


The Safe Default: A Hosted Storage Service

Just as a hosted auth provider takes password storage off your plate, a hosted storage service takes file handling off your plate: it stores the bytes, serves them through a CDN, enforces size limits, and can apply access rules per file. Your app keeps a reference — a URL or file key — in its own database, not the file itself.

All three keep the file itself out of your application code path after the initial upload. Writing your own upload handler that streams files to your own disk or object storage bucket is possible and sometimes right for larger apps, but it's much more surface area to get wrong — start with a hosted service unless you have a specific reason not to.


The Prompt to Ask For

Name the hosted service explicitly so AI doesn't default to writing a custom upload handler, and ask for access control and validation in the same prompt so neither is left as an afterthought:

Prompt

Add file upload to my app using [Supabase Storage / Cloudinary / UploadThing] for [profile photos / documents / attachments]. Do not write uploaded files to my own server disk or store their raw bytes in my database — only store the resulting URL or file key. Include: (1) an upload component that sends the file to the provider directly or through a signed upload URL, not through my backend as a raw buffer, (2) a maximum file size and an allowed file type list enforced by the provider or my server, not only by the file picker's `accept` attribute, (3) a default of private access for this file, with a signed, time-limited URL used to display it, unless there's a specific reason it should be public, and (4) a rule that only the file's owner can replace or delete it. Tell me which of these settings are configured in the provider's dashboard versus in my code.

When the AI returns code, check for these things before you consider it done:

  1. The file never passes through your own database as a blob — your database stores a URL or file key, the provider stores the bytes.
  2. A size limit and type restriction are enforced by the provider or your server, not just suggested by the file picker — a browser's accept attribute is a hint to the operating system's file dialog, not a check on what actually gets sent.
  3. Private files use signed or access-controlled URLs, not a public bucket with an unguessable name — the next section explains why that distinction matters.
  4. Only the uploading user can replace or delete their own file — the same ownership check you'd expect on any other user data.

Why a Hidden File URL Isn't the Same as a Private File

It's tempting to treat a long, random-looking file URL as private — nobody would guess it, so it feels safe. Don't rely on this. If the file lives in a public bucket, anyone who obtains that URL — from a shared link, a browser history, a referrer header, or a page that embeds the image — can view or download it forever, with no way for you to revoke access. An unguessable name changes who's likely to find the file; it does nothing to stop anyone who does.

Decide Public or Private Per File, Not Per App

A public marketing image and a user's private tax document are not the same kind of file, even if your app stores both in the same provider. Public assets — a logo, a shared blog header — can live in a public bucket with a plain URL. Anything tied to one user's account should default to private: access enforced by the storage provider's own rules (Supabase Storage policies, Cloudinary's signed delivery, UploadThing's access controls) and served through a URL that expires or that only resolves for the authenticated owner. Decide this per file type up front, not after a user finds someone else's file.

Test this the same way you'd test account isolation: upload a file as Account A, then try to fetch it directly — by URL, and by calling whatever endpoint serves it — while signed in as Account B, and again while signed out entirely. If the file was meant to be private, both attempts should fail. A file that only "looks" private because its URL is long isn't private — it's unlisted.

Prompt

Show me exactly how access is controlled for each file type my app stores. For anything tied to a single user's account, confirm the storage provider itself denies access to other users and to signed-out visitors — not just that the app doesn't link to the file. Give me a test: upload a file as one account, then show the requests I'd run to try to access it as a different account and while signed out, and what response each should return.


Validate What You Received, Not What Was Claimed

A file's name and extension are whatever the person uploading it typed, and the browser's reported content type is whatever the operating system guessed — neither is a guarantee of what the file actually is. A file named photo.jpg can contain anything. Ask AI to check the file's real content, not its label, before your app treats it as an image, a PDF, or anything else it displays or processes.


Keeping Storage and Cost Under Control

Unlike most features, uploads add to a bill that grows with usage and never shrinks on its own — every file kept is file storage you keep paying for, on every provider's free tier and beyond. Two habits keep this from surprising you:


Pre-Launch Uploads Checklist

Related Guides

Adding User Accounts to Your Vibe Coded App

Most uploads belong to a signed-in user — hosted auth, per-user data access, and the two-account isolation test this guide builds on.

Adding Payments to Your Vibe Coded App

The other feature where an AI shortcut has real consequences — hosted checkout, webhooks, and what to verify before real money moves.

Growing Your Vibe Coded App

Adding features, handling real users, and keeping an eye on hosting and storage costs as your app grows.

Sanitizing Code and Data Before Sending to AI

What to scrub before pasting code or credentials into an AI conversation — including storage keys.

Back to Home