AWS Builder Center
Securing and Optimizing Static Assets with Amazon S3 and CloudFront: A Beginner’s Guide

Securing and Optimizing Static Assets with Amazon S3 and CloudFront: A Beginner’s Guide

Learn how to securely host and deliver static assets using Amazon S3 and CloudFront. This beginner-friendly guide covers creating a private S3 bucket, implementing Origin Access Control (OAC) to block direct public access, and configuring a global CDN for blazing-fast, secure content delivery within the AWS Free Tier.

Information System Student
When building web applications, developers often need a place to store and serve static assets like images, videos, CSS, and JavaScript files. While Amazon S3 is an excellent storage solution, making an S3 bucket entirely public is generally discouraged due to security risks, potential data leaks, and higher latency for users located far from the bucket's region.
The AWS best practice is to pair a Private S3 Bucket with Amazon CloudFront, a global Content Delivery Network (CDN). By utilizing CloudFront's Origin Access Control (OAC), you ensure that your S3 bucket can only be accessed through the CDN, blocking any direct public access while delivering content with ultra-low latency worldwide.
In this guide, we will set up a secure asset delivery pipeline using AWS CLI and the Management Console.

Create a Private S3 Bucket

First, let's create a completely private S3 bucket. Unlike legacy tutorials, we will leave the Block Public Access settings strictly enabled.
Run the following AWS CLI commands to create your bucket. (Note: S3 bucket names must be globally unique, so add a random suffix).
1
2
3
4
5
6
7
export AWS_REGION="us-east-1"
export BUCKET_NAME="my-secure-assets-bucket-12345"

# Create the bucket
aws s3api create-bucket \
--bucket $BUCKET_NAME \
--region $AWS_REGION
Ensure that all public access is blocked at the bucket level (this is the default behavior, but it's good practice to enforce it explicitly):
1
2
3
aws s3api put-public-access-block \
--bucket $BUCKET_NAME \
--public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

Upload a Sample Asset

Let's create a simple hello.txt file and upload it to our private bucket.
1
2
3
4
5
echo "Hello from a secure S3 bucket behind CloudFront!" > hello.txt

# Upload to S3
aws s3 cp hello.txt s3://$BUCKET_NAME/hello.txt \
--content-type "text/plain"
If you try to access this file directly via its S3 URL (https://s3.amazonaws.com/my-secure-assets-bucket-12345/hello.txt), AWS will rightfully return a 403 Access Denied error because the bucket is completely private.

Create a CloudFront Distribution with OAC

Setting up a CloudFront distribution with Origin Access Control (OAC) involves generating specific routing rules. We'll use the AWS Management Console for this step to easily generate the necessary security policies.
  1. Navigate to the CloudFront Console and click Create Distribution.
  2. Origin Domain: Select your S3 bucket from the dropdown (my-secure-assets-bucket-12345.s3.us-east-1.amazonaws.com).
  3. Origin Access:
    • Select Origin access control settings (recommended).
    • Click Create control setting, leave the defaults, and click Create.
  4. Default Cache Behavior:
    • Under Viewer protocol policy, select Redirect HTTP to HTTPS for maximum security.
  5. Web Application Firewall (WAF): For this beginner tutorial (and to avoid WAF charges), you can select Do not enable security protections.
  6. Click Create Distribution.
After creation, CloudFront will display a yellow banner reminding you to update your S3 bucket policy. Click the Copy policy button.

Update the S3 Bucket Policy

For CloudFront to fetch files from your private bucket, S3 needs explicit permission to trust the CloudFront OAC identity.
Create a file named bucket-policy.json and paste the policy you copied from the console. It will look similar to this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Version": "2012-10-17",
"Statement": {
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-secure-assets-bucket-12345/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::YOUR_ACCOUNT_ID:distribution/YOUR_DISTRIBUTION_ID"
}
}
}
}
Apply this policy to your S3 bucket using the CLI:
1
2
3
aws s3api put-bucket-policy \
--bucket $BUCKET_NAME \
--policy file://bucket-policy.json

Test the Secure Delivery

Go back to the CloudFront console and copy your Distribution domain name (e.g., d12345abcdefg.cloudfront.net).
Note: It might take 3–5 minutes for the CloudFront distribution status to change from "Deploying" to "Enabled".
Test the asset delivery through the CDN using curl or your browser:
1
2
3
4
export CF_DOMAIN="d12345abcdefg.cloudfront.net"

# Fetch the file via CloudFront
curl https://$CF_DOMAIN/hello.txt
Success! You should see: Hello from a secure S3 bucket behind CloudFront!

Resource Cleanup

To avoid charges on your AWS account once you are done experimenting:
  1. Empty the S3 bucket:
1
aws s3 rm s3://$BUCKET_NAME --recursive
  1. Disable and Delete CloudFront Distribution:
    • Go to the CloudFront Console, select the distribution, and click Disable.
    • Wait a few minutes for it to fully disable, then click Delete.
  1. Delete the S3 Bucket:
1
aws s3api delete-bucket --bucket $BUCKET_NAME --region$AWS_REGION

Key Takeaways

  1. Zero Trust Storage: By enforcing Block Public Access and utilizing OAC, your S3 buckets are protected from direct web scraping and unauthorized public exposure.
  2. Cost & Performance Efficiency: CloudFront caches your files at edge locations globally. When users request an asset, it's served from the nearest geographical server, drastically reducing latency and S3 egress data transfer costs.
  3. Modern Standards: Origin Access Control (OAC) is the modern, more secure replacement for the deprecated Origin Access Identity (OAI), supporting AWS KMS-encrypted buckets and dynamic requests.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article