This project documents how I built and deployed my personal resume site on AWS. The goal was to create a lightweight portfolio that is easy to maintain, globally available, and backed by a simple serverless component for a real visitor counter.
The result is a static website architecture that is inexpensive to run, straightforward to update, and a practical example of combining core AWS services into a small production-style deployment.
The frontend was built with HTML, CSS, and a small amount of JavaScript. At a minimum, the deployment requires an index.html entry point and any supporting assets such as images, icons, or additional pages.
Because the site is static, updates are simple: modify the files locally and upload the latest version to the hosting bucket.
I created an S3 bucket aligned with the site domain and uploaded the static assets to it. S3 acts as the origin for the website content and provides a simple deployment target for future updates.
For a basic static hosting setup, the bucket can expose public read access to the site objects. An example policy is shown below:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mycoolwebsite.com/*"
}
]
}
I placed CloudFront in front of the S3 origin to improve global performance, enable HTTPS, and provide a cleaner public entry point for the website.
In this setup, the S3 website endpoint is used as the origin rather than the REST endpoint.
mycoolwebsite.com.s3-website-ap-southeast-1.amazonaws.comindex.htmlRoute53 was used to manage DNS for the custom domain. The public record is an A alias record pointing to the CloudFront distribution, which keeps the URL clean and avoids exposing the distribution domain to visitors.
Type: A (Alias)
Name: mycoolwebsite.com
Alias Target: dxxxxxxxxxxxx.cloudfront.net
To make the site slightly more dynamic, I added a visitor counter backed by DynamoDB and Lambda. Unlike a browser-only solution, this approach keeps a shared count that is visible to every visitor.
VisitorCount with a primary key like id.Example Lambda function (Node.js):
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
const params = {
TableName: 'VisitorCount',
Key: { id: 'main' },
UpdateExpression: 'ADD #c :inc',
ExpressionAttributeNames: { '#c': 'count' },
ExpressionAttributeValues: { ':inc': 1 },
ReturnValues: 'UPDATED_NEW'
};
const result = await dynamo.update(params).promise();
return {
statusCode: 200,
headers: { 'Access-Control-Allow-Origin': '*' },
body: JSON.stringify({ count: result.Attributes.count })
};
};
Example client-side request:
<script>
fetch('https://your-api-id.execute-api.ap-southeast-1.amazonaws.com/default/VisitorCounter')
.then(res => res.json())
.then(data => {
document.getElementById('visitors').textContent = data.count;
});
</script>
With this in place, each page load can retrieve and display a shared visitor count from the backend service.
After the infrastructure was configured, I validated the deployment by checking content delivery through the custom domain, confirming HTTPS redirection, and verifying that the visitor counter updated successfully through the API.
For troubleshooting, the main checkpoints were S3 object access, CloudFront origin settings, and Lambda or API Gateway logs.
This project was a useful exercise in building a small but complete cloud-hosted application. It combines static hosting, CDN delivery, DNS management, and serverless components in a way that is practical, low-cost, and easy to reason about.
Beyond serving as my personal website, it also works as a compact case study for deploying frontend assets on AWS and extending a static site with lightweight backend functionality.