S3 is the standard solution for file storage in web apps. This guide covers creating a bucket, setting up IAM permissions, generating presigned URLs for direct browser uploads, and serving files securely.
In this guide
In the AWS Console, go to S3 → Create bucket. Choose a globally unique name and your preferred region (same region as your server reduces latency and costs). Uncheck "Block all public access" only if you need public files (like user profile photos). For private files (documents, sensitive uploads), keep the block enabled and use presigned URLs to grant temporary access. Enable versioning for important files.
Never use root AWS credentials in your app. Create an IAM user in IAM → Users → Create user. Attach a custom policy: {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:PutObject","s3:GetObject","s3:DeleteObject"],"Resource":"arn:aws:s3:::your-bucket-name/*"}]}. Generate Access Key credentials. Store them in .env.local: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. This user can only access your specific bucket.
The recommended pattern: browser requests a presigned URL from your API, browser uploads directly to S3 (bypasses your server). In your API route: import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const url = await getSignedUrl(s3Client, new PutObjectCommand({ Bucket: "mybucket", Key: `uploads/${uuid()}.jpg`, ContentType: "image/jpeg" }), { expiresIn: 300 }). The presigned URL is valid for 5 minutes.
After getting the presigned URL from your API: const response = await fetch(presignedUrl, { method: "PUT", body: file, headers: { "Content-Type": file.type } }). On success (HTTP 200), save the S3 key (not the presigned URL — it expires) to your database. To generate a download URL later: getSignedUrl(s3Client, new GetObjectCommand({ Bucket, Key }), { expiresIn: 3600 }). This gives a 1-hour download link.
For publicly accessible files, put CloudFront (CDN) in front of S3. In AWS Console → CloudFront → Create Distribution, set the origin to your S3 bucket. Use an Origin Access Control to block direct S3 access (files only served through CloudFront). Files are cached at edge locations worldwide — a user in Tokyo gets your US-hosted files from a Tokyo server. CloudFront also handles SSL automatically.
Need Help?
Our engineering team handles implementations like this every week. Get a free scoping call — we will tell you exactly what it takes and what it costs.
Book a free callCompetitive Intelligence
Efficiency Modeling
© 2026 NexWorldTech — Built for Global Dominance.