Cost & Performance Wins with AWS Static Hosting

AWS S3 and CloudFront CDN

When launching a new website, developers often default to traditional server hosting or managed platforms. However, if your site is compiled into static HTML, CSS, and JS (the exact output generated by Vanilla Gorilla), you have access to a hosting architecture that is faster, more durable, and cheaper than almost any traditional server: Amazon Web Services (AWS) S3 combined with CloudFront CDN.

Let's break down the massive cost and performance advantages of hosting static sites in the cloud.

1. Cost Efficiency (Almost Free)

With traditional hosting, you pay a flat monthly rate for a virtual machine (VM) regardless of whether you receive 10 or 10,000 visitors. If your site gets a traffic spike, you might need to upgrade your tier to prevent it from crashing.

AWS serverless static hosting changes the economics completely:

  • Pay-For-Use Storage (Amazon S3): Storing a compiled static website usually takes up less than 50MB. S3 storage costs $0.023 per GB per month. Storing your site costs less than $0.01 per month.
  • Pay-For-Traffic Delivery (Amazon CloudFront): CloudFront has a generous Free Tier that includes 1 TB of data transfer out per month and 10 million HTTP requests. For the vast majority of personal blogs, portfolios, and small business sites, CloudFront delivery is 100% free.
  • Total Monthly Cost: Typically $0.50 (the cost of managing your DNS zone via AWS Route 53).

2. High-Performance Speed (Sub-Millisecond TTFB)

A traditional server must process incoming requests, fetch data from a database, assemble the HTML page, and send it back to the client. This introduces latency, measured as Time to First Byte (TTFB).

With S3 and CloudFront:

  • Content is Pre-Rendered: There is no database or server-side script to run. The HTML is already fully constructed.
  • Edge Caching: CloudFront is a Content Delivery Network (CDN) with hundreds of "edge locations" worldwide. When a visitor requests your page, CloudFront delivers the cached HTML from the server physically closest to them (e.g., in London, Tokyo, or New York).
  • Global Latency Reduction: TTFB drops from 200–500ms down to 10–30ms, making your page transitions feel instant.

3. Infinite Scalability & Durability

If your blog post goes viral and hits the front page of Hacker News or Reddit, a standard server will likely crash under the concurrent load.

An S3 and CloudFront setup is immune to this:

  • DDoS Protection: CloudFront natively integrates with AWS Shield, automatically absorbing and mitigating large-scale traffic floods.
  • Elastic Scaling: S3 and CloudFront handle tens of thousands of requests per second automatically. You don't have to configure load balancers, auto-scaling groups, or server memory limits.
  • 99.999999999% Durability: S3 replicates your files across multiple geographic data centers, ensuring your content is never lost due to hardware failure.

4. Setting Up AWS S3 & CloudFront

Before you can automate your deployment using GitHub Actions, you need to configure S3 and CloudFront in your AWS Management Console:

A. Configure the Amazon S3 Bucket

  1. Create a Bucket: Go to the S3 console and create a bucket named after your target domain (e.g., my-website.com) in the region closest to your target audience.
  2. Block Public Access: Keep "Block all public access" checked (this is highly recommended for security).
  3. Leave Everything else as default and click "Create Bucket" ⚠️Note: Do not enable "Static website hosting" in S3. Keeping it disabled forces traffic to go through CloudFront, which is more secure and cost-efficient.
  4. Click on your newly created bucket and navigate to the Permissions tab

B. Configure CloudFront Distribution with OAC

  1. Create Distribution: In the CloudFront console, click Create Distribution.
  2. Origin Domain: Click the text box and select your S3 bucket from the dropdown
  3. Origin access: Select Origin access control settings (recommended).
  • Click Create control setting. Leave the default settings (Sign requests) and click Create.
  1. Viewer protocol policy: Select Redirect HTTP to HTTPS (essential for security).
  2. Cache key and origin requests: Select Cache optimized (default).
  3. Web Application Firewall (WAF): Choose whether to enable security protections (you can select Do not enable for simple sites to save costs).
  4. Settings:
  • Default root object: Type index.html
  1. Save: Create the distribution and note down the Distribution ID and public domain name.
  2. Open CloudFront Console and click on Functions in the left sidebar.
    • Click Create function, give it a name (e.g., RedirectSubdirectoryIndexes), and select CloudFront Functions v1.0 (or newer).
    • Replace the boilerplate code with the following JavaScript:
    function handler(event) {
        var request = event.request;
        var uri = request.uri;
    
        // If the URI ends with a slash, append 'index.html'
        if (uri.endsWith('/')) {
            request.uri += 'index.html';
        } 
        // If the URI has no extension (pretty URL like /about), append '/index.html'
        else if (!uri.includes('.')) {
            request.uri += '/index.html';
        }
    
        return request;
    }
    
    • Click Save
    • Click Publish
    • Scroll down the page to Associated distributions and Click Add to distribution
    • Select your CloudFront distribution from the dropdown
    • Save the association

C. Authorize Cloudfront to Access Your S3 Bucket

Once your distribution is created, CloudFront will display a prominent banner at the top of the screen: "The S3 bucket policy needs to be updated..."

  1. Click the Copy policy button on the banner.
  2. Go back to your S3 Bucket → Permissions tab. Scroll down to Bucket policy and click Edit.
  3. Paste the copied JSON policy into the editor and click Save changes. This policy grants CloudFront's OAC permission to read objects (s3:GetObject) from your private bucket.

D. Set up IAM User to perform actions on your behalf

To authorize the GitHub Actions deploy workflow, you must generate an IAM User in AWS and configure their permissions. While you could use AWS managed policies like AmazonS3FullAccess and CloudFrontFullAccess, we strongly recommend applying a least-privilege policy to ensure security.

Create an inline policy or a custom managed policy in AWS IAM and attach it to your IAM User (e.g., Vanilla-Gorilla). Be sure to replace YOUR_BUCKET_NAME and YOUR_DISTRIBUTION_ID with your actual resources:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "S3SyncActions",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR_BUCKET_NAME",
                "arn:aws:s3:::YOUR_BUCKET_NAME/*"
            ]
        },
        {
            "Sid": "CloudFrontInvalidation",
            "Effect": "Allow",
            "Action": [
                "cloudfront:CreateInvalidation"
            ],
            "Resource": [
                "arn:aws:cloudfront::*:distribution/YOUR_DISTRIBUTION_ID"
            ]
        }
    ]
}

E. Collect GitHub Deployment Secrets

Collect the following credentials and details, and add them as secrets in your GitHub repository under Settings -> Secrets and variables -> Actions:

  • AWS_ACCESS_KEY_ID (Your IAM user access key)
  • AWS_SECRET_ACCESS_KEY (Your IAM user secret key)
  • AWS_REGION (e.g., us-east-1)
  • AWS_S3_BUCKET (e.g., my-website.com)
  • AWS_CLOUDFRONT_DISTRIBUTION_ID (Your CloudFront distribution ID)

You should collect this information and store it in your password manager or someplace safe as once you enter it into the GitHub secrets, you won't be able to retrieve the values and you don't want to have to generate new credentials each time.

F. (Optional) Custom Domain & SSL (HTTPS)

If you want to use a custom domain (e.g., my-website.com) instead of the default *.cloudfront.net URL:

  1. Request a Certificate: In the AWS Certificate Manager (ACM) console, request a public certificate. Enter your custom domain name (e.g., my-website.com) and the wildcard *.my-website.com. ACM will guide you through a validation process (usually DNS-based) to prove ownership of the domain. NOTE: you must request the certificate int he US East (N. Virginia) region (us-east-1) for it to be valid for CloudFront.
  2. Associate with CloudFront: Go back to your CloudFront distribution settings → General tab → Edit. Add your domain name to the Alternate domain names (CNAMEs) field.
  3. In the Custom SSL Certificate section, select the certificate you just created.
  4. Update DNS: In your DNS provider (e.g., AWS Route 53, GoDaddy), create a CNAME record pointing your custom domain (e.g., www.my-website.com) to your CloudFront distribution's domain name (e.g., XXXXXXXXXX.cloudfront.net).

How to Deploy Your Vanilla Gorilla Site

To take advantage of this serverless architecture, you have two primary deployment strategies:

Strategy A: Automated CI/CD via GitHub Actions (Recommended)

By far the safest and most robust strategy is to push your code to a remote GitHub repository and let GitHub Actions compile and deploy your site automatically.

  • Strong Suggestion (Backup Your Site): We highly recommend backing up your project source code to a private or public GitHub repository. This guarantees that your source content (like your markdown files and skeletal templates) remains safe even if your local computer experiences a failure.

  • Git Automation: The project comes with a .github/workflows/deploy.yml pipeline that triggers on commits to the main branch.

  • Command Your Agent: If you are developing using an agent-compatible editor (like Antigravity IDE), you don't even have to write git commands manually. You can simply command your AI coding assistant:

    "Build, commit, and deploy my changes to production."

    The agent will automatically build the static assets locally, verify everything is clean, commit your changes with a conventional commit message, and push them to your GitHub repository. The Action will then run, syncing ./dist/ to S3 and invalidating the CloudFront cache in minutes.

Strategy B: Local Command Line Deployment

If you are developing locally and want to push updates manually, make sure you have the AWS CLI installed and configured. Then, run the following steps:

  1. Compile: Run npm run build to generate the /dist/ folder.
  2. Sync: Upload the files inside /dist/ to your Amazon S3 bucket:
    aws s3 sync dist/ s3://my-website-bucket --delete
    
  3. Invalidate: Purge the CloudFront cache so visitors immediately see your new content:
    aws cloudfront create-invalidation --distribution-id MY_DIST_ID --paths "/*"
    

By removing the web server from the equation, you eliminate complexity, secure your data, speed up load times, and save money. It's the ultimate setup for the modern, vanilla web.