Storing Uploaded Files and Serving Them in Express
File uploads are everywhere in backend applications.
Examples:
profile pictures
PDFs
videos
resumes
product images
But many only focus on the upload part and forget an important question:
“Where do these files actually go after upload?”
And another one:
“How are they later accessible from the browser?”
To understand file uploads properly in Express, we need to understand:
storage
static file serving
public URLs
safe handling practices
What Happens During File Upload?
When a user uploads a file:
browser sends file data to server
Express receives the request
server stores the file somewhere
server later serves that file when requested
The important part is: uploaded files do not magically become accessible.
You must explicitly:
store them
expose them
serve them
Where Uploaded Files Are Stored
In many Express applications, uploaded files are stored inside a folder like:
project/
├── uploads/
│ ├── image1.png
│ ├── resume.pdf
│ └── avatar.jpg
├── server.js
This folder acts as local file storage.
Why Local Storage Exists
The server needs a physical location to save incoming files.
Without storage:
uploaded files disappear after request completion
users cannot access them later
Local storage is the simplest approach because files are stored directly on the server machine.
Example Upload Handling
Libraries like multer are commonly used.
Example:
const multer = require("multer");
const upload = multer({
dest: "uploads/"
});
This tells Express:
“Store uploaded files inside the uploads folder.”
Local Storage vs External Storage
There are two major storage approaches.
1. Local Storage
Files stored directly on server disk.
Example:
uploads/
Advantages
simple setup
fast for small projects
easy development workflow
Problems
server disk can fill up
scaling becomes difficult
files may disappear during deployments
multiple servers cannot easily share files
2. External Storage
Files stored in dedicated storage services.
Examples:
AWS S3
Cloudinary
Google Cloud Storage
Instead of saving files locally:
server uploads files to external provider
provider manages storage and delivery
Why External Storage Exists
Large-scale applications may handle:
millions of images
videos
large documents
Keeping everything on one server becomes unreliable.
External storage solves:
scalability
backups
CDN delivery
distributed access
Serving Static Files in Express
Uploading files is only half the system.
Users also need access to those files.
By default, Express does not expose folders publicly.
If an image exists inside:
uploads/avatar.png
the browser cannot access it automatically.
You must explicitly serve it.
Static File Serving
Express provides:
express.static()
Example:
app.use("/uploads", express.static("uploads"));
This creates a public route.
Now:
uploads/avatar.png
becomes accessible through:
http://localhost:3000/uploads/avatar.png
What Actually Happens?
Flow:
Browser requests image URL
↓
Express checks static folder
↓
Matching file found
↓
File returned as response
Express acts like a file server.
Why Static Serving Exists
Browsers need URLs, not file system paths.
The browser cannot access:
/home/project/uploads/avatar.png
But it can access:
https://example.com/uploads/avatar.png
Static serving maps server folders to public URLs.
Example Real-World Upload Flow
User uploads image
↓
Express receives file
↓
File stored in uploads folder
↓
Database stores file path
↓
Frontend receives image URL
↓
Browser requests image later
↓
Express serves static file
This is how profile pictures and uploaded content usually work.
Important Security Considerations
File uploads are dangerous if handled carelessly.
Because users can upload:
malicious files
huge files
executable scripts
fake file types
Safe handling is extremely important.
1. Validate File Types
Never trust file extensions alone.
Example bad upload:
virus.exe renamed to image.png
Always validate MIME types and allowed formats.
2. Limit File Size
Without limits:
attackers can upload huge files
server storage may crash
Example:
limits: {
fileSize: 5 * 1024 * 1024
}
This limits uploads to 5MB.
3. Rename Uploaded Files
Never trust original filenames directly.
Bad:
../../../system-file
Many systems generate unique filenames automatically to avoid:
collisions
path traversal issues
malicious naming
4. Avoid Public Access to Everything
Not every uploaded file should be publicly accessible.
Examples:
private documents
invoices
user reports
Some files should require authentication before serving.
5. Separate Uploads From Application Code
Good structure:
project/
├── src/
├── uploads/
├── public/
This avoids accidental execution or exposure of sensitive files.
Simple Mental Model
Think of uploaded files like this:
Uploading
“Store the file somewhere safely.”
Static Serving
“Create a public URL for accessing it.”
Security
“Never trust uploaded data blindly.”
File uploads in Express are more than just accepting files from users.
A complete upload system involves:
storage strategy
static serving
public access management
validation
security controls
For small applications, local storage is often enough.
For scalable production systems, external storage solutions are usually preferred.
The key realization is:
Uploading files and serving files are two separate responsibilities.
One stores data. The other exposes it safely to users.