
Why I Wouldn't Send Every File Through My Backend
I started looking at file uploads differently after learning how applications can use Amazon S3 directly. The idea of using a backend only to authorize an upload, while the actual file goes straight to S3, seemed like a small architectural change but made a lot of sense to me
File uploads look simple when you're building a small application.
The user selects a file, clicks upload, the backend receives it, and the backend stores it somewhere.
That's probably how I would have designed it without thinking twice.
But while learning about Amazon S3, I came across another pattern that made me rethink this.
The backend doesn't necessarily need to handle the entire file.
The approach I was used to
The first architecture that comes to mind is:
Browser → Backend → S3
The user uploads a file to the backend.
The backend receives the file and then sends it to S3.
For a small image, this might not seem like a big deal.
But imagine users uploading videos, PDFs, datasets, or other large files.
Now the backend is sitting in the middle of every upload.
It receives the entire file, holds the request open, and then sends the same data somewhere else.
That started to feel unnecessary.
Then I came across presigned URLs
A different approach is:
Browser → Backend
The backend checks whether the user is allowed to upload the file and generates a temporary presigned URL.
Then:
Browser → S3
The actual file goes directly to S3 using that URL.
The backend doesn't have to carry the file itself.
This was one of those AWS concepts that looked complicated at first but became pretty intuitive once I saw the flow.
Why keep the bucket private?
At first, it might seem easier to just make the S3 bucket public.
Then the browser could upload directly without all these extra steps.
But that creates a much bigger security problem.
A private bucket with a temporary presigned URL gives the application much more control.
The backend can decide:
- who is allowed to upload
- which object they can upload
- what operation the URL allows
- how long the URL should remain valid
The user doesn't need AWS credentials just to upload one file.
The backend still has an important job
The interesting part is that the backend isn't removed.
It just does a different job.
Instead of carrying the entire file, it handles the authorization and generates the temporary access URL.
So the flow becomes something like:
User → Backend → temporary permission → S3
User → S3 → actual file
That separation makes more sense to me than making the backend responsible for everything.
There is another benefit
This pattern also changes how I think about backend scalability.
If thousands of users upload large files, sending all those files through the application server means the server is doing a lot of work that S3 is already designed to handle.
With direct uploads, the storage layer handles the file transfer while the application focuses on the actual application logic.
It doesn't mean presigned URLs are the right answer for every upload.
There are still things to think about, such as file size limits, permissions, expiration times and validating what was uploaded.
But the architecture itself is much cleaner for many applications.
What I took away
The biggest thing I learned wasn't actually about S3.
It was about not making one component responsible for everything.
When I first started building applications, my backend was usually the middleman for almost everything.
Learning cloud architecture has made me question that approach.
Sometimes the better design is to let each service do the job it is designed for.
For file storage, that can mean letting S3 handle the file while the backend handles the authorization.
That seems like a small architectural decision, but it's one I would think about much earlier in a new project now.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article