From a Simple HTML File to AWS CloudFront: What Deploying My Portfolio Actually Taught Me
I recently decided to build a personal portfolio.
Nothing too complicated.
Just a simple website where I could show:
- Who I am
- What I'm learning
- My projects
- My technical skills
- How to contact me
At first, I thought the difficult part would be building the website.
It wasn't.
The real learning started when I tried to put it on the internet.
It Started With One HTML File
My first version was just:
index.html
No React.
No backend.
No database.
Just HTML and CSS.
It worked perfectly on my computer.
I opened it in the browser and thought:
"Okay, the website is done."
Then came the question:
"How do I actually deploy this?"
That's when I started exploring AWS.
My First Stop: Amazon S3
I created an S3 bucket and uploaded my index.html.
The idea seemed simple:
My computer
↓
index.html
↓
S3
↓
Internet
But when I tried accessing it, AWS gave me:
AccessDenied
My first reaction was basically:
"But the file is literally there."
And that's when I started understanding something important.
Having a file in the cloud doesn't automatically mean everyone can access it.
There are permissions.
There are policies.
There are access controls.
There is configuration.
The file existing and the file being publicly accessible are two different things.
Learning to Read the Error Instead of Panicking
I think this was one of the most useful parts of the entire process.
Instead of immediately changing random settings, I started looking at the actual error.
AWS was telling me:
AccessDenied
So I started asking:
Who is being denied?
What resource are they trying to access?
What permission is missing?
That changed how I approached the problem.
I wasn't just trying random solutions anymore.
I was trying to understand the system.
Then I Decided to Use React
After getting the basic website working, I wanted to turn it into a proper React project.
So I created a React + Vite application.
The structure became:
portfolio/
│
├── index.html
├── package.json
│
└── src/
├── main.jsx
└── style.css
Instead of writing everything directly into HTML, I started thinking in terms of components, data, and rendering.
For example, instead of manually writing every skill, I could represent them as data:
const skills = [
"Python",
"TensorFlow",
"Docker",
"Machine Learning"
];
and render them through React.
It wasn't a huge application, but it helped me understand why frameworks like React are useful.
The dist Folder Was Another Lesson
Then I learned something I hadn't really thought about before.
My React source code isn't what I necessarily need to deploy.
I ran:
npm run build
and Vite created:
dist/
├── index.html
└── assets/
That made something click for me.
React is helping me develop the application.
The browser ultimately needs the resulting HTML, CSS, JavaScript, and other assets.
So my deployment flow became:
React Source Code
↓
npm run build
↓
dist/
↓
S3
That distinction between development files and production build files was something I hadn't really understood before.
Then Came CloudFront
I didn't want to stop at S3.
I wanted to learn how CloudFront fits into the architecture.
So I added CloudFront.
Now I had:
Browser
↓
CloudFront
↓
S3
This looked pretty straightforward.
Until I opened my CloudFront URL.
404.
Instead of my portfolio:
404 Not Found
And then I saw:
Code: NoSuchKey
But the interesting part was this:
Key: index.html/index.html
I stared at that for a while.
My S3 bucket had:
index.html
not:
index.html/index.html
So why was CloudFront asking S3 for that?
The Problem Wasn't My React Code
This was probably my favorite debugging lesson.
I initially thought:
"Maybe my React build is broken."
It wasn't.
My index.html existed.
My build existed.
My S3 bucket had the files.
The problem was somewhere else.
I checked the CloudFront origin configuration.
And there it was.
I had configured the origin path as:
/index.html
while also using:
index.html
as the default root object.
So I had effectively told CloudFront:
"Start at index.html."
and then:
"Also use index.html as the root."
Result:
index.html/index.html
AWS wasn't being random.
I had configured it that way.
That was a good lesson.
Fixing It
I removed the origin path.
So instead of:
Origin path:
/index.html
I left it empty.
And kept:
Default root object:
index.html
The request flow then became:
/
↓
CloudFront
↓
index.html
↓
S3
instead of:
/
↓
CloudFront
↓
index.html
↓
index.html
↓
index.html/index.html
And Then... It Still Didn't Immediately Work
This was another AWS lesson.
I had fixed the configuration.
But CloudFront uses caching.
So the old response could still be cached.
I created a CloudFront invalidation:
/*
After that, I tested again.
And finally...
The portfolio loaded.
What I Learned From a "Simple Portfolio"
Looking back, the portfolio itself wasn't a technically huge project.
But the deployment taught me several things I wouldn't have learned by just following a tutorial.
1. Code isn't the entire application
There are many layers:
Code
↓
Build
↓
Files
↓
Storage
↓
Permissions
↓
CDN
↓
Caching
↓
Browser
A problem at any layer can make the entire website appear broken.
2. Errors are useful information
Before this, an error like:
NoSuchKey
would mostly mean:
"Something is wrong."
Now I try to ask:
"What key is AWS actually looking for?"
In my case, the answer was:
index.html/index.html
And that immediately gave me a direction.
3. Infrastructure has bugs too
As developers, we often think about bugs in code.
But configuration can have bugs too.
A wrong path.
A wrong bucket name.
A missing permission.
A caching problem.
These can break an application just as easily as a programming bug.
My Deployment Architecture
What started as a simple HTML file eventually became:
User
│
▼
CloudFront
│
▼
S3 Bucket
/ \
/ \
index.html assets/
And my development workflow is:
Write React
↓
Test locally
↓
npm run build
↓
dist/
↓
Upload to S3
↓
CloudFront
↓
Internet
The Bigger Lesson
I'm still learning.
I don't want to pretend that I suddenly understand AWS because I managed to deploy one website.
I still have a lot to learn about:
- Cloud architecture
- IAM
- Networking
- DNS
- HTTPS
- CI/CD
- Infrastructure as Code
- Monitoring
- Security
But I think I'm starting to understand something more important.
When you're learning development, getting stuck is part of the process.
The goal isn't to never get errors.
The goal is to become better at answering:
"Why did this happen?"
That's what I'm trying to get better at.
And this portfolio deployment was a surprisingly good first step.
Tech Stack
Frontend:
- React
- Vite
- JavaScript
- HTML
- CSS
AWS:
- Amazon S3
- Amazon CloudFront
Tools:
- VS Code
- npm
- Git/GitHub
What's Next?
The portfolio is deployed, but I'm not treating it as finished.
I want to keep improving it as I learn more.
Next, I'd like to explore:
Custom Domain
↓
HTTPS
↓
CI/CD
↓
GitHub → Automatic Deployment
Because the next goal isn't just:
"I deployed a website."
It's:
"I understand how the website gets from my code to a real user."
And that's a much more interesting thing to learn.



Top comments (0)