Permissions
↓
Static Website Hosting
I configured static website hosting and worked through the public-access configuration and bucket policy.
The important part of the bucket policy was allowing public GetObject access:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::kiran-2403717674421023-15092026/"
}
]
}
But then I ran into another issue.
- My Bucket Name Wasn't What I Thought
At one point I was working with:
kiransarathi k-bucket-portfolio
But when I looked at my AWS console, the actual bucket I was configuring was:
portfolio-nith
That caused confusion while creating the bucket policy.
AWS was telling me:
Policy has invalid resource
The reason was simple.
The policy resource has to reference the actual bucket name.
So:
arn:aws:s3:::kiran-2403717674421023-15092026/*
instead of:
arn:aws:s3:::kiran-2403717674421023-15092026/*
That was a small mistake, but it taught me something important:
Don't blindly copy AWS configuration. Understand what resource you're actually configuring.
- Then I Wanted to Use React
Once I had the basic portfolio working, I decided to convert it into a React application.
Instead of one large HTML file, I moved to a React + Vite structure:
portfolio/
│
├── index.html
├── package.json
│
└── src/
├── main.jsx
└── style.css
I used React to render the portfolio sections and JavaScript arrays for things like skills and projects.
For example:
const skills = [
"Python",
"C / C++",
"Data Structures & Algorithms",
"Machine Learning",
"TensorFlow",
"NumPy",
"Pandas",
"Docker"
];
And projects were represented as data:
const projects = [
{
title: "CLOTE",
description: "...",
tech: "Private Cloud • File Management"
}
];
This was my first step toward understanding how a real frontend project is structured rather than putting everything into one HTML file.
- Understanding npm run build
This was another important part.
I initially thought:
"Since it's React, I need a server running on AWS."
Not necessarily.
React can be built into static files.
I ran:
npm install
and then:
npm run build
Vite generated:
dist/
│
├── index.html
└── assets/
├── ...
└── ...
This was a major realization for me.
The browser doesn't need my React source code or Node.js running on S3.
The production build becomes static assets that can be served by a web server.
So the deployment becomes:
React source
↓
npm run build
↓
dist/
↓
AWS S3
- Adding CloudFront
Then I wanted to put CloudFront in front of S3.
The architecture became:
┌──────────────┐
│ Browser │
└──────┬───────┘
│
▼
┌──────────────┐
│ CloudFront │
└──────┬───────┘
│
▼
┌──────────────┐
│ S3 │
│ index.html │
│ assets/ │
└──────────────┘
This was the point where things got interesting.
- The CloudFront 404 Error
Instead of my portfolio, CloudFront returned:
404 Not Found
Code: NoSuchKey
Message: The specified key does not exist.
Key: index.html/index.html
At first I had no idea why AWS was asking for:
index.html/index.html
My S3 bucket clearly contained:
index.html
not:
index.html/index.html
So I started looking at the CloudFront configuration.
- Finding the Actual Problem
I checked the CloudFront origin configuration.
And there it was.
My origin looked like:
Origin type: S3 static website
Origin path: /index.html
That was the mistake.
CloudFront already knew that the default root object was:
index.html
And I had also told the origin to use:
/index.html
So CloudFront effectively ended up requesting:
/index.html/index.html
S3 obviously couldn't find that object.
The error suddenly made sense.
- The Fix
I changed the CloudFront origin path from:
/index.html
to:
(empty)
And kept the CloudFront default root object as:
index.html
So the configuration became conceptually:
CloudFront
Default root object:
index.html
Origin:
portfolio-nith S3 website endpoint
Origin path:
(empty)
Now the request flow made sense:
/
↓
CloudFront
↓
index.html
↓
S3
↓
portfolio
instead of:
/
↓
CloudFront
↓
index.html
↓
/index.html
↓
index.html/index.html ❌
- Why I Had to Invalidate CloudFront
Even after fixing the configuration, CloudFront can still have cached responses.
So I created a CloudFront invalidation:
/*
This tells CloudFront to remove the cached objects so that the next request can fetch the updated content.
- What I Actually Learned
The biggest thing I learned from this wasn't React.
It wasn't S3.
It wasn't even CloudFront.
It was debugging.
Before this, when something didn't work, my first instinct was often:
"The code must be wrong."
But this project showed me that problems can exist at completely different layers.
For example:
Frontend
↓
React
↓
Build
↓
Static files
↓
S3
↓
Permissions
↓
CloudFront
↓
Caching
↓
Browser
A website can have perfectly valid React code and still return a 404 because the infrastructure configuration is wrong.
- My Struggles
These were some of the problems I faced:
S3
AccessDenied
I had to understand bucket permissions and public access.
Bucket policy
Policy has invalid resource
I was referencing the wrong bucket name.
CloudFront
NoSuchKey
Key: index.html/index.html
I had incorrectly configured the origin path.
Caching
Even after fixing the configuration, CloudFront could still serve an old cached response.
- What I Would Do Differently Next Time
If I were starting this deployment again, my process would be:
- Build React application ↓
- Test locally ↓
- npm run build ↓
- Check dist/ ↓
- Upload dist contents to S3 ↓
- Configure S3 ↓
- Configure CloudFront ↓
- Test CloudFront ↓
- Check logs/errors if something fails ↓
- Invalidate cache when required
And most importantly:
I would change one configuration at a time and test after each change.
- Final Architecture
The final concept I learned is:
┌───────────────────┐
│ User │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ CloudFront │
│ CDN │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ S3 Bucket │
│ │
│ index.html │
│ assets/ │
└───────────────────┘
My React application is built into static files and served through AWS infrastructure.
Final Thoughts
This was a relatively small project, but it gave me something more valuable than just a portfolio.
It gave me experience with the uncomfortable part of development:
things not working.
I had to read errors, inspect configuration, understand what AWS was actually doing, find the mismatch, and fix it.
The index.html/index.html error in particular was frustrating at first.
But once I understood why it happened, it became one of the most useful lessons from the project.
I'm still learning AWS, React, and software development, but I'm slowly trying to replace:
"Copy the code and hope it works"
with:
"Understand what each component is doing, then debug when it doesn't."
And I think that's a much better way to learn development.
Tech Stack
Frontend
React
Vite
HTML
CSS
JavaScript
Cloud
Amazon S3
Amazon CloudFront
Development
VS Code
npm
Git/GitHub







Top comments (0)