Hosting a Secure Static Website with S3 and CloudFront: Part III

This is the last in our three part series where we discuss the creation of a private, secure, static website using Amazon S3 and CloudFront.

Introduction

Amazon S3 and CloudFront are powerful tools for hosting static websites, but configuring them securely can be surprisingly confusing-even for experienced AWS users. After implementing this setup for my own use, I discovered a few nuances that others often stumble over, particularly around CloudFront access and traffic routing from VPC environments. This post aims to clarify these points and highlight a potential gap in AWS’s offering.

The Secure S3 + CloudFront Website Setup

The typical secure setup for hosting a static website using S3 and CloudFront looks like this:

  1. S3 Bucket: Store your website assets. Crucially, this bucket should not be publicly accessible.
  2. CloudFront Distribution: Distribute your website content, with HTTPS enabled and custom domain support via ACM.
  3. Origin Access Control (OAC): Grant CloudFront permission to read from your private S3 bucket.
  4. S3 Bucket Policy: Configure it to allow access only from the CloudFront distribution (via OAC).

This setup ensures that even if someone discovers your S3 bucket URL, they won’t be able to retrieve content directly. All access is routed securely through CloudFront.

The VPC Epiphany: Why Is My Internal Traffic Going Through NAT?

For many AWS users, especially those running workloads inside a VPC, the first head-scratcher comes when internal clients access the CloudFront-hosted website. You might notice that this traffic requires a NAT gateway, and you’re left wondering:

  • “Isn’t this all on AWS’s network? Why is it treated as public?”
  • “Can I route CloudFront traffic through a private path in my VPC?”

Here’s the key realization:

CloudFront is a public-facing service. Even when your CloudFront distribution is serving content from a private S3 bucket, your VPC clients are accessing CloudFront through its public endpoints.

  • CloudFront -> S3: This is private and stays within the AWS network.
  • VPC -> CloudFront: This is treated as public internet traffic, even though it often stays on AWS’s backbone.

This distinction is not immediately obvious, and it can be surprising to see internal traffic going through a NAT gateway and showing up with a public IP.

CloudFront+S3+WAF

Why This Feels Like a Product Gap

For my use case, I wasn’t interested in CloudFront’s global caching or latency improvements; I simply wanted a secure, private website hosted on S3, with a custom domain and HTTPS. AWS currently lacks a streamlined solution for this. A product offering like “S3 Secure Website Hosting” could fill this gap by combining:

  • Private S3 bucket
  • Custom domain + HTTPS
  • Access control (VPC, IP, IAM, or WAF)
  • No CloudFront unless explicitly needed

Securing Access to Internal Clients

To restrict access to your CloudFront-hosted site, you can use AWS WAF with an IPSet containing your NAT gateway’s public IP address. This allows only internal VPC clients (routing through the NAT) to access the website while blocking everyone else.

Conclusion

The S3 + CloudFront setup is robust and secure - once you understand the routing and public/private distinction. However, AWS could better serve users needing simple, secure internal websites by acknowledging this use case and providing a more streamlined solution.

Until then, understanding these nuances allows you to confidently deploy secure S3-backed websites without surprises.

Disclaimer

This post was drafted with the assistance of ChatGPT, but born from real AWS battle scars.

If you like this content, please leave a comment or consider following me. Thanks.


Next post: Hosting a Secure Static Website with S3 and CloudFront: Part I

Previous post: Rob’s Rule of Three