Day-1:Cloud Workshop
I Built and Deployed My First React Portfolio on AWS S3 + CloudFront - And Broke It Along the Way As a Computer Science student, I’ve been trying to move away from just writing code by following tutorials and actually understand how things work by building and deploying projects. One of the things I wanted to build was a simple personal portfolio. It sounded easy: Build a React portfolio → upload it to AWS → get a public URL. It wasn't quite that simple. I ran into permissions issues, S3 configuration problems, CloudFront errors, and eventually discovered that I had accidentally configured CloudFront to look for: index.html/index.html This is the story of how I built it, broke it, debugged it, and finally understood what was actually happening. - Starting With a Simple HTML Portfolio I initially created my portfolio using a single: index.html I kept the design intentionally simple. The portfolio contained: - About Me - Technical Skills - Projects - Education - Areas of Interest - Contact Some of the projects and areas I included were: - CLOTE - self-hosted cloud file management - Automated Data Intelligence System - Graph Algorithm Visualizer - ML Ad-Click Prediction - City-Specific Smart Traffic System - Knowledge-Based Systems At this point, everything worked locally. I could simply open the HTML file in my browser and see the portfolio. But then I wanted to actually deploy it. 2. Moving to AWS S3 I decided to use Amazon S3 for hosting. I created an S3 bucket and uploaded my portfolio. The object looked like: s3://boopesh-09-150926/index.html My first assumption was: "The file is uploaded, so anyone should be able to open it." That assumption was wrong. When I tried accessing the website, I received: AccessDenied Access Denied # 3. First Struggle - S3 AccessDenied This was my first real AWS deployment problem. The HTML itself wasn't the issue. The problem was with S3 permissions and website access configuration. I had to understand the relationship between: text S3 Bucket ↓ Object ↓ 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: json { "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::portfolio-nith/" } ] } But then I ran into another issue. 4. My Bucket Name Wasn't What I Thought At one point I was working with: nithish-bucket-portfolio But when I looked at my AWS console, the actual bucket I was configuring was: my-portfolio 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:::portfolio/* instead of: text arn:aws:s3:::boopesh-09-150926/* That was a small mistake, but it taught me something important: Don't blindly copy AWS configuration. Understand what resource you're actually configuring. 5. 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. 6. 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 7. 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. 8. 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. 9. 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. 10. 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 ❌ 11. 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. 12. 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. 13. 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. 14. What I Would Do Differently Next Time If I were starting this deployment again, my process would be: 1. Build React application ↓ 2. Test locally ↓ 3. npm run build ↓ 4. Check dist/ ↓ 5. Upload dist contents to S3 ↓ 6. Configure S3 ↓ 7. Configure CloudFront ↓ 8. Test CloudFront ↓ 9. Check logs/errors if something fails ↓ 10. Invalidate cache when required And most importantly: I would change one configuration at a time and test after each change. 15. 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)
Comments
No comments yet. Start the discussion.