All posts

Your SaaS File Uploads Are Slower Than They Need to Be

Speeding file upload in your saas...

Voltage·April 27, 2026·10 min read
file_upload

Here's a question most developers never think to ask: when a user uploads a file in your app, where does that file actually go first?If you're using any standard SDK setup - multer in Express, Django's request.FILES, Rails's ActionDispatch - the answer is: through your server. The file lands on your server, sits in memory or a temp directory, and then your server streams it up to S3 or R2 or whatever storage backend you're using.That flow looks like this:

And it causes three problems that most SaaS developers quietly accept as normal:

1. Latency doubles. The file has to travel to your server and then from your server to storage. Two hops instead of one. For a 10MB file, that's noticeable. For a 100MB file, it's painful.

2. You pay for bandwidth you didn't need to use. Every byte that passes through your server costs you compute time and egress fees, depending on your hosting setup. You're essentially paying to be a middleman.

3. Your server becomes a bottleneck. Multiple concurrent large uploads can saturate your server's network interface or exhaust memory. You end up scaling your API servers to handle file upload load, which is a genuinely expensive solution to a problem that shouldn't exist.

The alternative: presigned URLs

Cloud storage providers solved this years ago. The idea is simple: instead of accepting the file on your server and forwarding it, your server generates a short-lived signed URL that gives the client direct write access to a specific location in storage. The client then uploads directly.The flow becomes:

Your server's only job is to generate the URL. The actual file transfer bypasses it entirely.Here's what that looks like in practice with Cloudflare R2 as an example:

The client gets a URL, uploads directly to R2, and your server never touches the file data. Upload speed improves significantly because you've eliminated one of the two network hops. No extra bandwidth costs. No memory pressure on your API server.

Why most developers still don't use presigned URLs

Knowing about presigned URLs is one thing. Actually using them consistently in a multi-tenant SaaS is another.The moment you move beyond a single-user app, you run into questions that presigned URLs alone don't answer:Tenant isolation. If every user uploads to uploads/${filename}, you immediately have a collision problem. So you build a namespacing convention: tenants/${userId}/${filename}. Fine - but now you're generating keys, managing that convention in every part of your codebase that touches storage, and hoping a bug never lets a key from one tenant resolve to another tenant's path.Quota enforcement. Presigned URLs are issued before the upload happens. If User A has used 4.8GB of their 5GB quota and tries to upload a 500MB file, you need to check that before issuing the URL, not after. So now you need to track per-tenant storage consumption in your database, update it after every upload, decrement it after every delete, and handle the race conditions when multiple uploads happen simultaneously.Usage tracking. Your product probably wants to show users how much storage they've consumed. That means a database column, a counter that stays in sync with actual storage, and a query path to surface that data per tenant. Another moving part.None of this is impossibly hard. But it's a week of work that has nothing to do with your actual product, and it's work you'll repeat on the next SaaS you build too.

Solving the whole problem at once

This is the gap Tenantbox fills. It's a thin API layer that handles the multi-tenancy concerns, isolation, quota enforcement, usage tracking while also keeping the presigned URL architecture intact.You send it a tenant_id and a filename. It returns a presigned URL. Your client uploads directly to Tenantbox storage. Your server never touches the file. But the namespacing, the quota check, and the usage update all happen automatically on Tenantbox's side.

No tenant setup in advance, the first upload for a tenant_id creates the tenant automatically. Quota limits are enforced before the URL is issued, so an over-quota upload fails at the request stage, not mid-transfer. Storage consumption is tracked per tenant without any database work on your side.The $0 egress fee is worth noting too because files go client → Tenantbox storage directly.