Skip to content
← Back to Notes

Deploy to S3 + CloudFront

Starting point: npm run build has been run. The out/ folder exists with all static HTML, JS, CSS, and assets.
End point: Site live on a CloudFront HTTPS URL.


Part 1 — S3 Bucket

Step 1: Create the bucket

  1. Open AWS Console → S3 → Create bucket
  2. Bucket name: sonkar-me
  3. AWS Region: pick your closest region (e.g. ap-south-1 for Mumbai)
  4. Under "Block Public Access settings for this bucket" — uncheck "Block all public access"
  5. Tick the acknowledgement checkbox that appears
  6. Leave everything else as default
  7. Click Create bucket

Step 2: Enable static website hosting

  1. Click into the sonkar-me bucket → Properties tab
  2. Scroll to the bottom → Static website hosting → click Edit
  3. Select Enable
  4. Index document: index.html
  5. Error document: 404.html
  6. Click Save changes

After saving, scroll back to Static website hosting — copy the Bucket website endpoint URL. It looks like:

http://sonkar-me.s3-website.ap-south-1.amazonaws.com

Keep this URL handy for CloudFront setup.


Step 3: Set bucket policy (public read)

  1. Permissions tab → scroll to Bucket policy → click Edit
  2. Paste the following policy exactly:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::sonkar-me/*"
    }
  ]
}
  1. Click Save changes

The bucket Permissions tab will now show a red "Publicly accessible" badge — this is expected and required.


Step 4: Upload the out/ folder contents

  1. Click the Objects tab → Upload
  2. Click Add files and Add folder
  3. Upload the contents inside out/ — not the out/ folder itself. Your upload should include:
    • index.html
    • about/ (folder)
    • notes/ (folder)
    • work/ (folder)
    • 404.html
    • _next/ (folder — contains all JS/CSS chunks)
    • images/, fonts/, robots.txt, sitemap.xml, etc.
  4. Leave all upload settings as default — S3 auto-detects content types
  5. Click Upload and wait for it to complete (may take a minute with _next/ assets)

Tip: On Windows, open out/ in Explorer, select all files and folders inside it (Ctrl+A), then drag them into the S3 upload interface.


✅ Checkpoint 1 — Test S3 directly

Visit the bucket website endpoint URL you copied in Step 2 (http://sonkar-me.s3-website...).

You should see your portfolio homepage. Click through to /about/, /notes/, a note article — all should work.

If you get 403 Forbidden: the bucket policy in Step 3 is missing or wrong.
If you get 404: the index.html at root was not uploaded, or static website hosting isn't enabled.


Part 2 — CloudFront Distribution

Step 5: Create the distribution

  1. Open AWS Console → CloudFront → Create distribution

  2. Origin domain:

    • Do not select from the dropdown (it will suggest the S3 REST endpoint which breaks directory routing)
    • Manually paste the S3 website endpoint URL from Step 2, without http://:
    sonkar-me.s3-website.ap-south-1.amazonaws.com
    
  3. Protocol: select HTTP only
    (S3 website endpoints only speak HTTP — CloudFront handles HTTPS for visitors)

  4. Origin path: leave empty


Step 6: Configure viewer settings

Still on the same Create distribution page, scroll to Default cache behavior:

  1. Viewer protocol policy: Redirect HTTP to HTTPS
  2. Allowed HTTP methods: GET, HEAD
  3. Cache policy: CachingOptimized (default)
  4. Origin request policy: leave as None

Step 7: Distribution settings

Scroll to Settings:

  1. Default root object: index.html
  2. Price class: Use only North America and Europe (cheapest) — or choose All Edge Locations for best global performance
  3. WAF: skip for now (leave disabled)
  4. Description (optional): sonkar-me portfolio

Click Create distribution.


Step 8: Configure custom error pages (critical)

After the distribution is created:

  1. Click into your distribution → Error pages tab → Create custom error response

  2. Add this response:

    Field Value
    HTTP error code 403
    Customize error response Yes
    Response page path /404.html
    HTTP response code 404
  3. Click Create custom error response, then add a second one:

    Field Value
    HTTP error code 404
    Customize error response Yes
    Response page path /404.html
    HTTP response code 404

This handles deep-link navigation — e.g. typing yoursite.com/notes/bloom-filters/ directly into a browser. Without this, CloudFront returns a 403 instead of routing to the page.


✅ Checkpoint 2 — Wait for deployment

  1. Go to CloudFront → Distributions
  2. Your distribution shows Status: Deploying — wait until it changes to Enabled (typically 5–15 minutes)
  3. Copy the Distribution domain name — it looks like:
    d1a2b3c4defgh.cloudfront.net
    

✅ Checkpoint 3 — Verify the live site

Visit https://d1a2b3c4defgh.cloudfront.net (your actual domain).

Test these specifically:

  • Homepage loads (/)
  • /about/ loads
  • /notes/ loads
  • A note article loads e.g. /notes/bloom-filters/
  • /work/ loads
  • HTTPS padlock is present
  • Typing a URL directly (not clicking a link) works — this confirms error pages are set up correctly

Part 3 — Redeploying after changes

When you update the site (npm run build → new out/ folder):

Step 9: Re-upload to S3

  1. S3 → sonkar-meObjects tab → select all → Delete
  2. Re-upload the new out/ contents (same as Step 4)

Step 10: Invalidate CloudFront cache

CloudFront aggressively caches files. After uploading new files, old versions are served until the cache expires. Force an immediate refresh:

  1. CloudFront → your distribution → Invalidations tab → Create invalidation
  2. Object paths: /*
  3. Click Create invalidation

Wait ~30 seconds. The new version is now live.

Cost note: AWS gives 1,000 invalidation paths free per month. /* counts as 1 path, so normal deployments are free.


Summary

out/ folder
  → uploaded to S3 bucket "sonkar-me" (static website hosting ON, public read policy)
    → CloudFront distribution (origin = S3 website endpoint, HTTP only)
      → Viewer: HTTPS redirect, custom error pages → /404.html
        → Live at https://dXXXXXXXXXXXX.cloudfront.net

Next: Setting Up Custom Domain — connect sonkar.dev to this distribution using Route 53 and an ACM certificate.