Using CloudFront Signed URLs to Serve Private S3 Content

Jeremy Louie
Roam and Wander
Published in
4 min readJul 11, 2018

AWS S3 is a reliable service for storing and serving content. You can even serve private S3 content with pre-signed S3 URLs. However, S3 content is served from a single geographic location. For users not close to the S3 bucket’s region this can mean hundreds of milliseconds in latency per request. This is where a content delivery network (CDN) like AWS CloudFront comes in.

Configuring CloudFront to serve public content from an S3 bucket is pretty straight forward. If you want to serve private content through CloudFront setup is more complicated. Below are the steps needed for CloudFront to serve private S3 content through signed CloudFront URLs.

  1. Create a S3 Bucket
  2. Create a CloudFront Distribution
  3. Create a CloudFront Key Pair
  4. Generate Signed CloudFront URLs

1. Create a S3 Bucket

If you don’t already have a S3 bucket to store your content, you’ll need to create one. You can create a bucket through the AWS console or CLI.

Create a S3 bucket using AWS console

To create a bucket through the AWS console, go to the S3 management console and click the “Create Bucket” button. Enter a bucket name (ie. special-bucket-name), choose a region, and click “Create.” The default bucket options will keep the content private by default, so no need to walk through the other steps.

An even easier way to create a S3 bucket is using the AWS CLI. Simply run the command below:

aws s3 create-bucket --bucket special-bucket-name --region us-east-1

After the bucket is created, the bucket URL is “BUCKET_NAME.s3.amazonaws.com” (ie. “special-bucket-name.s3.amazonaws.com”). You should upload some files for testing but you won’t be able to access them directly through the bucket URL (this is where signed URLs come in).

2. Create a CloudFront Distribution

Now that you have a S3 bucket, you can create a CloudFront distribution. Using the CLI for this is a bit complex, so it’s easiest to use the AWS console.

Go to the CloudFront management console, click the “Create Distribution” button, and choose a web distribution. The settings that you need for the distribution are as follows:

  • Origin Domain Name: URL to the S3 bucket (ie. “special-bucket-name.s3.amazonaws.com”)
  • Restrict Bucket Access: “Yes”
  • Origin Access Identity: “Create a New Identity”
  • Grant Read Permissions on Bucket: “Yes, Update Bucket Policy” — updates the bucket policy so that CloudFront can access it.
  • Restrict Viewer Access: “Yes”
  • Trusted Signers: “Self”
  • Everything else can be left as default, but I recommend enabling “Redirect HTTP to HTTPS”, “Object Caching”, “Compress Objects Automatically” (aka gzip).

After creating a CloudFront distribution it will take some time (usually about 15 minutes) for initialization to complete. To avoid random issues, make sure the distribution status is “Deployed” before you start accessing it.

3. Create a CloudFront Key Pair

S3 URLs can be signed by an IAM user using s standard AWS API key and secret. Unfortunately, to sign CloudFront URLs you have to use a “CloudFront Key Pair.” To make it this even more challenging…

“IAM users can’t create CloudFront key pairs. You must log in using root credentials to create key pairs.” — AWS Docs

Once you’re logged in using root credentials, follow these steps:

  1. Go to the AWS account security credentials page.
  2. Expand “CloudFront key pairs” and click the “Create New Key Pair” button.
  3. From the opened dialog, download and save the generated private key file and public key file.
  4. Close the dialog, and save the “Access Key ID of the key pair you just generated.

You’ll use the Access Key ID and the contents of the private key file to create signed CloudFront URLs.

4. Generate Signed CloudFront URLs

Now that everything is set up you can finally sign CloudFront URLs. AWS has code examples for Perl, PHP, C#/.NET, and Java. For NodeJS/JavaScript you can use the outdated aws-cloudfront-sign NPM module or the AWS JavaScript SDK. To use the SDK, you’ll need to use the AWS.CloudFront.Signer class like the example below.

That’s it! 🎉 The objects in your S3 bucket are secure and can be accessed via signed CloudFront URLs.

--

--

Jeremy Louie
Roam and Wander

Full stack engineer at Roam & Wander's NYC office.