# Free UploadThing Alternative: Direct Uploads to R2

> UploadThing's free plan stores 2GB, then it's $10 a month. Send files straight from the browser to your own R2 or S3 bucket, never through your server.

Source: https://boringpush.com/blog/free-uploadthing-alternative-r2-s3

## Who this is for

You're building a Next.js app where people upload files: avatars, PDFs, screenshots, exports. You don't want those bytes crawling through your server, and you'd rather the files sat in a bucket you control than in a service's storage account.

This post follows one upload from click to saved file, then covers the limits and the honest case for staying on UploadThing.

## What UploadThing costs

From [uploadthing.com/pricing](https://uploadthing.com/pricing), checked on 2026-10-21: the 2GB plan is $0 a month, with unlimited uploads and downloads. The 100GB plan is $10 a month, and usage-based pricing starts at $25 a month with 250GB included, then $0.08 per GB over.

| | UploadThing | BoringPush storage |
|---|---|---|
| Platform fee | $0 plan, then $10–25/mo | None |
| Where files live | Their storage | Your own R2 or S3 bucket |
| Free ceiling | 2GB, shared between apps | Your bucket's own limit |
| What your server does | Holds the auth logic | Approves each upload, never holds the file |
| Dashboard and components | Yes | A drop-in upload box, no file dashboard |

That's the trade. UploadThing sells the dashboard, the components and the storage. Self-hosting gives you the bucket and the control.

## What you get

Files go from your visitor's browser straight to your own Cloudflare R2 or Amazon S3 bucket. Your server never carries a single byte of the file, so big uploads don't eat your memory.

Your server's job is smaller and more important: it decides who may upload what.

## One upload, in order

**1. The browser asks permission.** Your app checks the person is signed in, and checks the file's type and size against your rules.

**2. Your server hands out a short-lived link.** It's good for about two minutes, for one file, of one type. The file's name is chosen by your server, so nobody can slip a path like `../../` into the bucket.

**3. The browser sends the file straight to the bucket.** A progress bar runs from 0 to 100.

**4. Your server checks what actually arrived.** It reads the real type and size from the bucket and deletes the file if it breaks your rules. The browser check is a courtesy; this one protects your bucket and your bill.

Downloads and deletes ask your server too, and a person can only touch files in their own folder. Someone else's file gets a refusal.

## One place sets every limit

By default your app accepts PNG, JPEG, WebP, GIF and PDF files, up to 10MB each, with PDFs allowed up to 25MB. Files are sorted into folders for avatars, uploads and documents.

Change a limit once and every part of the upload flow follows it. Requests are also rate-limited, so one address can't hammer the upload route more than 20 times a minute.

## A drop-in upload box

The upload box supports drag and drop, a progress bar for each file and image previews. A failed upload gets a retry button, and keyboard users can open it with Enter or Space.

```jsx
<FileDropzone
  folder="avatars"
  onUploadComplete={(files) => saveAvatarKey(files[0].key)}
/>
```

You place it where your feature needs it, and keep the returned key in your own table. No extra packages are needed on your side.

## R2 or S3

Pick Cloudflare R2 or Amazon S3 with one setting. They work the same way for your users, and any S3-compatible bucket does too. You bring the bucket and its keys.

## Know this first

BoringPush doesn't give you a file manager. It returns a key for each upload, and your own table is where you keep the owner and what the file belongs to.

There's no list of uploaded files on its own, and the upload box ships ready to use in the demo, but you place it in your own pages. UploadThing's free plan is also a real offer: 2GB with unlimited uploads gets you components, a dashboard and support for nothing. If your app fits inside that, it's a fair choice.

## Why BoringPush is the way to do this

Files are your customers' trust. They should sit in a bucket you control, move directly from the browser, and never pass through a service whose pricing can change.

BoringPush gives you the safe version already built: signed links, your own limits, a check on what arrived and folders nobody else can open. No per-gigabyte meter from us, and no third party between your users and their files.

## FAQ

### Does my server ever hold the file?

No. It approves the upload and gives out a link. The browser sends the file to the bucket, and the bytes never pass through your server.

### What stops a user uploading a 500MB file?

The browser check stops honest users. For everyone else, your server reads the real size from the bucket after the upload and deletes the file if it's over the limit for its type.

### Can one user touch another user's files?

No. Each person's files live under their own folder, and a download, delete or check for anyone else's file is refused on your server.

### Do I need the AWS SDK?

No. Uploads use a plain browser upload to a signed link, and the signing runs on built-in tools. It works with any S3-compatible bucket.

### Where is my list of uploaded files?

Wherever you put it. The upload gives you a key, and your own table keeps the key, the owner and whatever the file belongs to.

## Keep reading

- [Free Uploadcare Alternative: Direct Uploads to R2](/blog/uploadcare-alternative-nextjs-r2) — the same bucket, a different vendor
- [500MB Uploads, 0 Server RAM](/blog/nextjs-r2-s3-uploads-no-ram) — why direct uploads fix the memory problem
- [Free Firebase and Supabase Alternative: SQLite You Own](/blog/free-firebase-supabase-alternative-sqlite) — where your keys can live

[Get BoringPush →](/#get)
