Create AWS CloudFront on Pay-as-You-Go (not the Free Plan)

Create CloudFront pay-as-you-go via AWS CLI.

Page content

The AWS Free plan is not working for me and Pay-as-you-go is hidden for new CloudFront Distributions on AWS Console .

When you host Hugo-based sites on AWS, you typically put the bucket behind CloudFront and attach a Lambda@Edge function so that requests to directory paths (e.g. /blog/) are rewritten to /blog/index.html for S3 website-style origins. You can then use hugo deploy or deploy with the AWS CLI to sync the built site. The catch: you need a CloudFront distribution that uses that Lambda and, if you want to avoid flat-rate plan commitments, pay-as-you-go pricing. The console no longer lets you choose that-only Free or Pro plans.

Documentation is not matching the source actual deliveries

This post shows how to create a pay-as-you-go distribution by reusing an existing distribution’s config via the AWS CLI and the scripts in this article’s scripts folder.

When the console only offers Free or Pro

Amazon introduced CloudFront flat-rate pricing plans (Free, Pro, Business, Premium) that bundle the CDN with WAF, DDoS protection, Route 53, and other services for a fixed monthly price with no overages. In the CloudFront console, when you create a new distribution, you now see only Free or Pro (and higher) plan options. The Pro plan starts at $15/month; there is no visible “pay-as-you-go” option there.

Pay-as-you-go is still the default when you create a distribution through the CloudFront API or AWS CLI (create-distribution). So if you already have a distribution created earlier (or via CLI) and want another one with the same behaviour—e.g. S3 website origin plus Lambda@Edge for index.html—you can export that distribution’s config, adjust it, and create a new distribution. The new one will be pay-as-you-go unless you attach it to a flat-rate plan later.

What you need

  • An existing CloudFront distribution whose config you can copy (e.g. one used for another Hugo site, or one created before the console change).
  • AWS CLI installed and configured with credentials that can run cloudfront:GetDistributionConfig and cloudfront:CreateDistribution.
  • jq (or similar) to edit the JSON config.

Example scripts used below live in this post’s scripts folder: current-distribution-config.json, new-distribution-config.json, updated-distribution-config.json, and final-distribution-config.json.

Step 1: Get the existing distribution config

Get the full distribution config (including ETag) and save it:

# Replace ABCDEFG with your existing distribution ID
aws cloudfront get-distribution-config --id ABCDEFG > current-distribution-config.json

get-distribution-config returns a wrapper object with ETag and DistributionConfig. For creating a new distribution you only pass the inner DistributionConfig, so we extract it:

jq '.DistributionConfig' < current-distribution-config.json > new-distribution-config.json

At this point new-distribution-config.json looks like the following (abbreviated). It includes CallerReference, Aliases, Origins, DefaultRootObject, and DefaultCacheBehavior with Lambda@Edge. For the example site domain I out micro.com:

{
  "CallerReference": "8b2b19eb-6d41-4fc0-8d04-a1313e23e2d7",
  "Aliases": {
    "Quantity": 1,
    "Items": ["micro.com"]
  },
  "DefaultRootObject": "index.html",
  "Origins": {
    "Quantity": 1,
    "Items": [
      {
        "Id": "micro.com.s3.us-west-2.amazonaws.com",
        "DomainName": "micro.com.s3-website-us-west-2.amazonaws.com",
        "OriginPath": "",
        "CustomOriginConfig": {
          "HTTPPort": 80,
          "HTTPSPort": 443,
          "OriginProtocolPolicy": "http-only"
        }
      }
    ]
  },
  "DefaultCacheBehavior": {
    "TargetOriginId": "micro.com.s3.us-west-2.amazonaws.com",
    "ViewerProtocolPolicy": "redirect-to-https",
    "LambdaFunctionAssociations": {
      "Quantity": 1,
      "Items": [
        {
          "LambdaFunctionARN": "arn:aws:lambda:us-east-1:...your-lambda-arn",
          "EventType": "origin-request",
          "IncludeBody": false
        }
      ]
    }
  }
}

Step 2: Set a unique CallerReference

Every distribution must have a unique CallerReference. Reusing the same value as another distribution causes the API to reject the request. Use a new string or a timestamp:

NEW_CALLER_REF="new-distr-$(date +%s)"
jq --arg ref "$NEW_CALLER_REF" '.CallerReference = $ref' new-distribution-config.json > updated-distribution-config.json

Step 3: Adjust aliases and origins (optional)

If the new site uses a different domain or S3 bucket, update Aliases and Origins in updated-distribution-config.json to match (e.g. new bucket website endpoint and alias list). If you want a distribution without a custom domain for now, clear aliases:

jq '.Aliases = {"Quantity": 0, "Items": []}' updated-distribution-config.json > final-distribution-config.json

If you need to point to a different S3 website endpoint, edit the Origins section in the same file (e.g. change Id and DomainName to the new bucket’s website hostname, and set DefaultCacheBehavior.TargetOriginId to that same Id).

Step 4: Create the new distribution

Create the distribution from the final config:

aws cloudfront create-distribution --distribution-config file://final-distribution-config.json

The response includes the new distribution’s Id, ARN, DomainName, and Status. The new distribution uses pay-as-you-go pricing by default. You can then point your DNS (or Route 53) to the new CloudFront domain and, if you use it for Hugo, deploy with Hugo deploy or AWS CLI as usual.

Quick reference: full script

Pasted together (using an existing distribution ID and clearing aliases), the flow is:

# 1) Export and extract DistributionConfig
aws cloudfront get-distribution-config --id EABCDEFGZ > current-distribution-config.json
jq '.DistributionConfig' < current-distribution-config.json > new-distribution-config.json

# 2) Unique CallerReference
NEW_CALLER_REF="new-distr-$(date +%s)"
jq --arg ref "$NEW_CALLER_REF" '.CallerReference = $ref' new-distribution-config.json > updated-distribution-config.json

# 3) Optional: no custom domain
jq '.Aliases = {"Quantity": 0, "Items": []}' updated-distribution-config.json > final-distribution-config.json

# 4) Create (pay-as-you-go) distribution
aws cloudfront create-distribution --distribution-config file://final-distribution-config.json

Pay-as-you-go vs flat-rate (recap)

  • Pay-as-you-go: You pay for data transfer out and requests; CloudFront’s perpetual free tier includes 1 TB data transfer and 10M HTTP/HTTPS requests per month. No monthly plan fee.
  • Flat-rate plans: Documented here. Free plan has lower allowances (e.g. 1M requests, 100 GB); Pro starts at $15/month with more capacity and bundled WAF, Route 53, etc. No overages.

If your traffic fits within the pay-as-you-go free tier and you don’t need the bundled flat-rate features, creating distributions via the CLI as above keeps you on pay-as-you-go while the console only shows Free/Pro.