DEV Community

Cover image for Hosting My Portfolio on AWS: A Serverless Journey with S3, CloudFront, and Lambda
2 1 1 1

Hosting My Portfolio on AWS: A Serverless Journey with S3, CloudFront, and Lambda

This article describes how I hosted my static portfolio website, which is a static website in the cloud using Amazon Web Services, leveraging serverless technology.

Thanks to Namecheap's free domain for students, I designed the website with Canva and developed it using Bootstrap 5, which has a 12-column Layout for easy mobile responsiveness.

Here's how I did it, the challenges I faced, and why AWS Serverless was the perfect fit.

These are the following services I used to make it possible.

Amazon S3: To store my website files, including HTML files, static folder (CSS, JS files), assets folder, and a resume.
Amazon CloudFront: To make the site globally available at greater speed using caching optimization.
Amazon Certification Manager: To obtain the SSL certificate for enabling https security on my site.
Amazon API Gateway: To make the lambda function available as an endpoint to the client side.
Amazon Lambda: To process the request and send a pre-signed URL of the resume stored in S3.
Amazon DynamoDB: To store the email IDs with timestamps for future use.

The architecture I used:

Reference Architecture

First, create a bucket in your preferred region and upload all the required files. Block all public access because we going to let only CloudFront access the bucket through OAC (Origin Access Control).

Bucket Items

Bucket Items

Block Public Access

Block Public Access

Bucket Policy

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::harsha-resume-doc/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::533267145529:distribution/E2NCAYZ67PRE3K"
                }
            }
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Create your free SSL certificate using ACM and verify it through your DNS provider. In my case, Namecheap's control panel, adding some DNS records for verification. After verification, the certificate is provided, and we are ready to use it for the next step.

SSL Certificate Issued

Note: You can do this step only if you are creating the request in N. Virginia region, i.e. us-east-1.

Create a CloudFront Distribution with Origins as the bucket. Attach the SSL certificate you got using the previous step, and more importantly about the price class, I went with all edge locations for best performance.

Origin as your bucket

Redirect your domain to the CloudFront by inserting the CNAME record in the DNS server in Namecheap. Now, you can view your website being hosted by pasting your domain name in the browser.

DNS Server Configuration

Let's add functionality to our resume download button.
Create a DynamoDB table with Email ID as a primary key for storing the email IDs of the users.

NoSQL Table is created

Create a proper execution role for the Lambda function with access to S3 and DynamoDB using AWS IAM service.

Create a Lambda function to get the request, process it, and store it in the DynamoDB table. Then, return a pre-signed URL to the resume back to the user for downloading it within 5 minutes (300 seconds).
Lambda Code in Python

import json
import boto3
import datetime
import os

# Initialize AWS clients
dynamodb = boto3.resource("dynamodb")
s3 = boto3.client("s3")

# Environment variables (set these in Lambda)
TABLE_NAME = os.environ["DYNAMODB_TABLE"]  # DynamoDB Table Name
S3_BUCKET = os.environ["BUCKET_NAME"]     # S3 Bucket Name
RESUME_KEY = os.environ["RESUME_KEY"]               # File key in S3

def lambda_handler(event, context):
    try:
        # Parse request body
        body = json.loads(event["body"])
        email = body.get("email")

        if not email:
            return {"statusCode": 400, "body": json.dumps({"message": "Email is required"})}

        # Store email & date in DynamoDB
        table = dynamodb.Table(TABLE_NAME)
        table.put_item(
            Item={
                "email": email,
                "date": str(datetime.date.today())
            }
        )

        # Generate a pre-signed URL for the resume
        presigned_url = s3.generate_presigned_url(
            "get_object",
            Params={"Bucket": S3_BUCKET, "Key": RESUME_KEY},
            ExpiresIn=300  # URL valid for 5 minutes
        )

        return {
            "statusCode": 200,
            "body": json.dumps({"message": "Success", "url": presigned_url})
        }

    except Exception as e:
        print(f"Error: {str(e)}")
        return {"statusCode": 500, "body": json.dumps({"message": "Internal server error"})}

Enter fullscreen mode Exit fullscreen mode

Create an HTTP API using API Gateway and add a route request-resume with a POST Request. Integrate the API with the Lambda function and add this API as a form action in the download button submission form.

HTTP API is created

Ta-da!!! You will get your portfolio hosted completely using Serverless technology using AWS, and you can put this in your resume.

Errors I have to face:

  1. Wrong access control of bucket Items in S3 Solved by attaching a bucket policy allowing CloudFront access via OAC.
  2. Unable to cloudfront distribution Opened an AWS support ticket for account verification (resolved in 24 hours).

The cost this project incurs is based on the number of visitors to the site. If the number is below 10, it will be very low. As the data moving out of the cloud will incur cost. And yes, we still cache the site at the edge locations using CDN.

You can access the site via this link

Future considerations for enhancing the project:

  • Using AWS SES, sending the resume via the mail to the users, and adding a follow-up mail
  • Adding AWS WAF protection and its captcha in forms can secure our site against vulnerabilities.

Top comments (0)