Securing AWS S3 and CloudFront for Blazing-Fast Static Website Hosting
The gold standard for hosting secure, fast static websites on AWS is coupling an Amazon S3 private bucket with Amazon CloudFront CDN using Origin Access Control (OAC). The S3 bucket has public access 100% blocked, preventing direct access and data scraping, while CloudFront caches assets globally at 450+ edge locations with TLS 1.3 encryption and custom domain certificates from AWS Certificate Manager (ACM).
Static websites and Single Page Applications (SPAs built with React, Vue, Next.js static exports, or Vite) do not need dedicated Linux servers. Hosting them on Amazon S3 delivered through Amazon CloudFront delivers millisecond global latency, infinite scalability, and virtually zero cost. Decoupling your static frontend from your backend APIs is standard enterprise practice; to design the corresponding backend API layer, pair this with our AWS High-Availability Web Application Architecture Blueprint.
However, many guides incorrectly tell developers to enable "Static Website Hosting" on S3 and disable "Block Public Access". This exposes your bucket publicly to web scrapers and unauthorized downloads. In this guide, we configure the modern, enterprise-approved pattern: keeping S3 100% private and restricting traffic exclusively to CloudFront via Origin Access Control (OAC).
▼ (Signed OAC Token)
[ Amazon S3 Bucket (100% Private, Public Access Blocked) ]
▲
[ Direct Public Traffic ] ➔➔➔ [ 403 Forbidden Access Denied ]
01. Step 1: Create a Secure, Private S3 Bucket
In the AWS Management Console, create an S3 bucket in your primary region:
- Bucket Name: Use a globally unique name (e.g.,
waseem-portfolio-production-assets). - Block Public Access: Keep "Block all public access" checked (ENABLED). Never turn this off.
- Bucket Versioning: Enable versioning for rollback protection against accidental deletions.
- Default Encryption: Enable server-side encryption with Amazon S3-managed keys (SSE-S3).
02. Step 2: Create a CloudFront Distribution with Origin Access Control (OAC)
Navigate to CloudFront → Create distribution:
- Origin domain: Select your newly created S3 bucket from the dropdown list.
- Origin access: Select Origin access control settings (recommended). Click "Create control setting" and accept the defaults.
- Viewer protocol policy: Select Redirect HTTP to HTTPS.
- Allowed HTTP methods:
GET, HEAD, OPTIONS. - Default root object: Enter
index.html.
03. Step 3: Attach the Restrictive S3 Bucket Policy
CloudFront generates an IAM Bucket Policy snippet. Copy and paste this policy into your S3 Bucket → Permissions → Bucket Policy:
{
"Version": "2012-10-17",
"Statement": {
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::waseem-portfolio-production-assets/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
}
}
}
}
AWS introduced Origin Access Control (OAC) to replace legacy Origin Access Identity (OAI). OAC supports server-side encryption with AWS KMS, supports all AWS regions (including opt-in regions), and uses modern SigV4 request signing.
04. Step 4: Handling SPA Client-Side Routing (React / Vue / Angular)
If you deploy a Single Page Application, refreshing a route like /dashboard or /profile triggers an S3 404 (NoSuchKey) error because S3 looks for a real directory path.
To resolve this in CloudFront: Navigate to Custom error responses → Create custom error response:
- HTTP error code:
403: Forbiddenand404: Not Found - Customize error response: Yes
- Response page path:
/index.html - HTTP Response code:
200: OK
Static Web Hosting Security Architecture Comparison
| Hosting Method | S3 Bucket Policy | HTTPS / Custom Domain | Global Edge Caching | Security & DDoS Rating |
|---|---|---|---|---|
| Legacy S3 Web Hosting | Public Read (Block Public Access OFF) | HTTP only (no free SSL on apex domains) | None (single-region latency) | ⚠️ Dangerous (bucket scraping & DDoS risk) |
| CloudFront + OAI (Legacy) | Private (Origin Access Identity) | Free ACM SSL Certificate | Global PoPs (400+ locations) | ⚠️ Deprecated (does not support KMS or POST) |
| CloudFront + OAC (Modern) | Private (Origin Access Control) | Free ACM SSL Certificate & HTTP/3 | Global Edge Caching with Origin Shield | 🛡️ Industry Standard (Zero public S3 access) |
- ↗ AWS CloudFront Origin Access Control (OAC) Guide — Amazon's security documentation on restricting direct S3 access and enforcing OAC.
- ↗ Amazon S3 Security Best Practices — Official security checklist for blocking public access and encrypting cloud buckets.
05. Frequently Asked Questions (FAQ)
/* to purge the edge cache globally within 60 seconds. In CI/CD pipelines, execute: aws cloudfront create-invalidation --distribution-id <ID> --paths "/*". You can automate this entire testing, building, and edge cache invalidation process on every Git commit using our tutorial on Building a Production CI/CD Pipeline with GitHub Actions and Docker.06. Conclusion & Next Steps
Migrating static frontends from traditional virtual servers to Amazon S3 paired with CloudFront and Origin Access Control provides unmatched benefits: sub-second global load times, automatic SSL certificate provisioning, zero server maintenance, and ironclad security that completely seals off direct public bucket access.
To eliminate manual updates, integrate this architecture directly with a GitHub Actions workflow that automatically compiles your frontend build, syncs assets to S3, and triggers an instant edge cache invalidation on every git push.
Ready to secure your frontend distribution with CloudFront OAC, origin shielding, and SSL encryption? Explore production web hosting architectures in the Waseem Kaluwal Portfolio, or connect on the Consultation Page to fortify your static edge delivery.
Related Cloud & DevOps Engineering Guides
Supercharge your infrastructure and deployment workflow with these companion production tutorials:
No comments:
Post a Comment