Host a Modern Web App on AWS for Pennies: S3 + CloudFront
Many junior developers default to running an EC2 instance or a container to host a portfolio or frontend side project. But if your app is built with React, Vue, Vite, or plain HTML/CSS, hosting on a traditional server means paying for idle CPU cycles. The battle-tested alternative: Amazon S3 for static storage paired with Amazon CloudFront for global content delivery and free SSL.
Why This Stack Wins
- Cost: Practically $0 per month for personal portfolios or prototypes.
- Speed: CloudFront caches assets at edge locations worldwide, drastically cutting latency.
- Maintenance: Zero operating system updates, zero server patches, zero downtime from memory leaks.
Step 1: Set Up Your S3 Bucket
- Open the Amazon S3 Console and click Create bucket.
- Pick a unique name (e.g.,
my-dev-portfolio-2026) and your preferred AWS region. - Keep Block all public access enabled. We will route all traffic securely through CloudFront rather than exposing the bucket directly to the internet.
Step 2: Build and Upload Your Assets
Run your framework’s build command locally to generate your static output folder (usually
dist or build):1
2
3
4
5
# Build your static files
npm run build
# Sync the output directly to S3 via the AWS CLI
aws s3 sync ./dist s3://my-dev-portfolio-2026/ --delete
Step 3: Distribute Globally with CloudFront
- Open the CloudFront Console and click Create distribution.
- Origin Domain: Select your newly created S3 bucket from the dropdown.
- Origin Access Control (OAC): Select Origin access control settings (recommended) and click Create new OAC. This generates a policy so only CloudFront can read from your S3 bucket.
- Viewer Protocol Policy: Set to Redirect HTTP to HTTPS.
- Default root object: Enter
index.html. - Click Create distribution.
Step 4: Apply the S3 Bucket Policy
CloudFront provides a ready-made bucket policy after creation. Copy it and navigate back to your bucket:
- In S3, open your bucket's Permissions tab.
- Under Bucket policy, click Edit and paste the policy:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Version": "2012-10-17",
"Statement": {
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-dev-portfolio-2026/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::<ACCOUNT_ID>:distribution/<DISTRIBUTION_ID>"
}
}
}
}
Replace
<ACCOUNT_ID> and <DISTRIBUTION_ID> with your specific values.Quick Pro-Tip: Single Page App (SPA) Routing
If you are using React Router or Vue Router, a direct reload on a nested route (e.g.,
/about) can throw a 403 Forbidden or 404 Not Found from S3.Fix it in one click:
- In your CloudFront distribution, open the Error pages tab.
- Click Create custom error response.
- Map HTTP error codes
403and404to return/index.htmlwith an HTTP response code of200: OK.
Over to You
Setting up this architecture teaches core cloud concepts—storage, caching, edge networks, and IAM policies—while keeping your monthly bill at zero.
Builders: Have you migrated a static site to S3 and CloudFront, or are you still running it on a VPS? What challenges did you encounter? Let's discuss in the comments below!
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article