
August 17, 2026
8 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
AWS S3 static website hosting with CloudFront CDN is the industry-standard architecture for serving secure, low-latency content without managing servers. While S3 provides durable object storage, exposing buckets directly via the legacy website endpoint creates security risks and lacks HTTPS support at the edge. For any production deployment in 2026, you must front your bucket with CloudFront using Origin Access Control (OAC) to enforce private access while delivering cached assets globally.
I have deployed this exact pattern for legal-tech portals and documentation sites where uptime and security are non-negotiable. Unlike traditional shared hosting or VPS setups discussed in AWS cloud hosting comparisons, this serverless approach eliminates OS patching and PHP-FPM tuning entirely. The trade-off is initial configuration complexity, but once established, it scales indefinitely with near-zero marginal cost for read-heavy workloads.
Why use AWS S3 static website hosting with CloudFront CDN instead of direct S3?
Direct S3 website hosting is effectively deprecated for production use. It supports only HTTP, exposes your bucket name publicly, and offers no DDoS protection or edge caching. CloudFront solves these deficiencies by acting as a secure reverse proxy layer between users and your origin.
The critical distinction in 2026 is Origin Access Control (OAC). Previously, developers used Origin Access Identity (OAI), which required complex bucket policies referencing specific CloudFront IDs. OAC simplifies this by allowing CloudFront to sign requests natively. Your bucket policy now simply permits cloudfront.amazonaws.com as a service principal, making infrastructure-as-code significantly cleaner and more portable across environments.
Performance gains are substantial. On a recent legal information portal I maintained, moving from direct S3 to CloudFront reduced Time-to-First-Byte (TTFB) from ~400ms to under 50ms for cached assets in South Asia. For Nepal-based users, the Mumbai and Kolkata edge locations provide excellent latency without requiring local infrastructure investment.
How do you configure S3 and CloudFront securely with OAC?
Security misconfiguration is the most common failure mode. Never enable "Static website hosting" on the S3 bucket when using CloudFront; that feature is for direct HTTP access only. Instead, keep the bucket private and let CloudFront handle all public-facing concerns.
Step 1: Create the private S3 bucket
Block all public access at the account level via S3 Block Public Access settings, then create your bucket with default encryption enabled. Upload your built static assets (HTML, CSS, JS, images). Ensure your build process generates cache-busted filenames (e.g., app.a1b2c3d4.js) so CloudFront can safely cache immutable assets forever.
<!-- Example bucket policy allowing ONLY CloudFront OAC -->
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-static-site-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789:distribution/EXXXXXXXXX"
}
}
}
]
} Step 2: Provision ACM certificate in us-east-1
CloudFront requires SSL certificates from the us-east-1 region exclusively. Request a certificate via AWS Certificate Manager covering both example.com and *.example.com. Validate via DNS (fastest) or email. This step blocks many deployments because engineers request certs in their home region (e.g., ap-south-1) and cannot select them in CloudFront.
Step 3: Configure CloudFront distribution with OAC
In the CloudFront console or Terraform/Pulumi config:
- Origin domain: Select the S3 bucket REST endpoint (
bucket.s3.region.amazonaws.com), NOT the website endpoint. - Origin access: Choose "Origin access control settings" → Create new OAC setting → Sign requests with SigV4.
- Viewer protocol policy: Redirect HTTP to HTTPS.
- Allowed HTTP methods: GET, HEAD, OPTIONS (never enable PUT/DELETE for static sites).
- Cache policy: Use "CachingOptimized" for assets; create custom policy for HTML with shorter TTL.
- Custom SSL certificate: Select your us-east-1 ACM cert.
- Default root object: Set to
index.html.
After creation, CloudFront displays a "Copy bucket policy" button. Apply this exact policy to your S3 bucket. This completes the OAC trust chain.
How do you handle SPA routing and error pages correctly?
Single-page applications (React, Vue, Laravel Livewire hybrid) break on CloudFront because requesting /about returns a 403/404 from S3 — there is no physical /about object. You must configure custom error responses to rewrite these to /index.html while preserving the browser URL.
Configure two custom error responses in CloudFront:
- 403 Forbidden: Response page =
/index.html, HTTP response code =200, Cache TTL =0. - 404 Not Found: Same configuration as above.
The zero-second TTL is critical. Without it, CloudFront caches the index.html response for errors, causing subsequent legitimate 404s to return stale content. For true 404 pages (missing images, broken links), handle those in your application router after hydration. This pattern works identically for Vue Router, React Router, and Alpine.js-based navigation systems I've implemented in Laravel Livewire projects.
For multi-page static sites (Hugo, Jekyll, plain HTML), skip this entirely. Each route has a physical file, and CloudFront serves them directly. Only SPAs need the error-response rewrite trick.
What is the optimal caching strategy for performance and freshness?
Blindly caching everything causes stale content disasters. A tiered cache policy separates immutable assets from mutable documents.
| Asset Type | Cache-Control Header | CloudFront TTL | Rationale |
|---|---|---|---|
| JS/CSS/Images (hashed) | public, max-age=31536000, immutable | 1 year | Filename changes on rebuild; safe to cache forever |
| Fonts (woff2) | public, max-age=31536000, immutable | 1 year | Rarely change; critical render path |
| HTML documents | public, max-age=0, must-revalidate | 0 seconds | Always check origin for updates; prevents stale routes |
| JSON/API manifests | public, max-age=60 | 1 minute | Short grace period for deployment propagation |
| robots.txt / sitemap.xml | public, max-age=3600 | 1 hour | Crawlers tolerate slight staleness |
Set these headers during your build/upload step, not in CloudFront behaviors. CloudFront respects origin headers by default when using managed cache policies. If you need to override, create a custom cache policy rather than modifying behavior settings directly — this keeps configurations reusable across distributions.
For invalidation after deploys, avoid wildcard /* invalidations on large sites; they're expensive ($0.005 per path after first 1,000) and slow. Instead, invalidate only /index.html and any changed HTML routes. Hashed assets never need invalidation because their URLs change. This approach keeps monthly costs predictable even with frequent deployments.
How does AWS S3 static website hosting with CloudFront CDN compare to alternatives?
Choosing the right platform depends on team expertise, budget, and operational tolerance. I've used all three patterns below for different client scenarios.
For Nepal-based businesses targeting local audiences, consider bandwidth costs carefully. AWS charges ~$0.085/GB from Mumbai/Kolkata edges to internet. A site serving 500GB/month costs ~Rs 5,500 (~$42) in transfer alone. Vercel's free tier includes 100GB; beyond that, Pro plans start at $20/month with 1TB included. For low-traffic brochure sites (50GB/month), AWS wins on absolute cost. For medium-traffic sites with unpredictable spikes, managed platforms cap financial risk.
Operational overhead matters too. With AWS, you own SSL renewal monitoring, WAF rule updates, and cache invalidation scripting. Managed platforms abstract this entirely. I recommend AWS S3 static website hosting with CloudFront CDN primarily when you already operate within AWS (RDS, Lambda, SES) and want unified billing/IAM, or when traffic volume justifies the engineering investment. For standalone marketing sites or portfolios, Vercel or Netlify deliver faster time-to-value.
Deploying and maintaining your static site reliably
Manual uploads via AWS Console are unacceptable for production. Automate deployments through CI/CD pipelines that build, test, and sync assets atomically. I use GitLab CI with the AWS CLI for most projects, following patterns similar to those in my CI/CD pipeline guides.
# .gitlab-ci.yml snippet for S3 + CloudFront deploy
deploy:
stage: deploy
image: amazon/aws-cli:latest
script:
- aws s3 sync dist/ s3://$BUCKET_NAME --delete --cache-control "public,max-age=31536000,immutable" --exclude "*.html" --exclude "sitemap.xml" --exclude "robots.txt"
- aws s3 sync dist/ s3://$BUCKET_NAME --delete --cache-control "public,max-age=0,must-revalidate" --include "*.html" --include "sitemap.xml" --include "robots.txt"
- aws cloudfront create-invalidation --distribution-id $CF_DIST_ID --paths "/index.html" "/sitemap.xml" "/robots.txt"
only:
- main The dual-sync strategy applies correct cache headers per file type without relying on S3 metadata overrides. The --delete flag removes orphaned files from previous builds, preventing bloat and potential security exposure of old assets. Invalidation targets only mutable entry points; hashed assets self-version.
Monitor actively. Enable CloudFront real-time logs to S3 or Kinesis for debugging cache misses and 4xx spikes. Set up CloudWatch alarms on 5xx error rates (>1% threshold) and origin latency (>500ms p95). These catch misconfigurations before users report issues. For cost governance, create AWS Budgets alerts at 50%/80%/100% of expected monthly spend — unexpected traffic surges or cache misconfigs can inflate bills overnight.
Finally, document your infrastructure. Store Terraform/Pulumi state remotely, version-control bucket policies, and maintain runbooks for certificate renewal and disaster recovery. Infrastructure-as-code isn't optional for AWS S3 static website hosting with CloudFront CDN; it's what separates maintainable systems from fragile ones that break when the original engineer leaves.
Moving forward with confidence
AWS S3 static website hosting with CloudFront CDN delivers enterprise-grade performance and security when configured correctly with OAC, proper caching tiers, and automated deployments. The initial setup demands precision, but the result is a resilient, scalable foundation that handles everything from personal portfolios to high-traffic legal portals without server management overhead.
If you're evaluating this architecture for a Nepal-based project or need help migrating an existing site to this pattern, reach out to discuss your specific requirements. I regularly help teams implement secure, cost-effective static hosting that aligns with their operational capacity and growth trajectory.

