Use presigned URLs when a web app only needs user files to land in object storage, and reach for a proxy upload through your backend when you must inspect, rewrite, or serialize the bytes before they're stored. For ordinary SaaS document and media uploads that's the presigned path almost every time, and for a dull reason: your API server never touches the payload, so it can't be the thing that runs out of memory at 3 a.m.

I've built both. The choice itself is easy — what people underestimate is everything the backend still owes the user after the browser starts talking to storage directly.

What each upload path actually costs you

A proxy upload charges you for the same bytes twice, once inbound to your app server and once outbound to the bucket, and that's before you count the request slot. A 200 MB video going through a proxy holds a worker, a connection, and usually a chunk of RAM for as long as the user's hotel wifi takes to push it. On a serverless runtime the arithmetic gets worse, because a lot of gateways cap request bodies somewhere around 6 MB and bill by request duration, so a proxy upload turns a cheap storage write into an expensive compute event that also happens to fail on large files. Direct-to-storage moves that whole cost line off your infrastructure: the app issues a short-lived URL, which is a few hundred bytes of JSON, and the file goes wherever the storage provider's edge terminates it.