Written by Mateus Alexandre, DevOps Engineer at TrackIt

Content Delivery Networks (CDNs) are essential for improving the performance, reliability, and scalability of web applications by distributing content across edge locations worldwide. Two of the most widely used CDNs in the industry are Fastly and Amazon CloudFront.

Fastly is known for its powerful VCL (Varnish Configuration Language) engine, allowing highly customizable logic at the edge. CloudFront, part of the AWS ecosystem, has matured rapidly with features like response headers policies, cache policies, and origin request controls — making it possible to build robust edge configurations with minimal custom code.

This article explores a real-world scenario where a Single Page Application (SPA) delivery setup — originally implemented with Fastly’s VCL — is transitioned to Amazon CloudFront, demonstrating how to replicate existing behaviors without relying on Lambda@Edge.

Why Migrate From Fastly to CloudFront?

While Fastly provides advanced edge logic capabilities through VCL, the decision to adopt Amazon CloudFront can be based on several strategic and operational factors:

  • Better integration with existing AWS infrastructure
  • Centralized billing and simplified cost management
  • Mature built-in policies for cache control, security headers, and origin behavior

These features make CloudFront a compelling choice for organizations seeking tighter cloud integration and robust edge performance without the need for custom edge scripting.

Architecture Overview Before Migration

The application in question is a Single Page Application (SPA) hosted on an Amazon S3 bucket and served via Fastly using the S3 REST API endpoint as the origin. Key characteristics of the deployment include:

  • TLS enforced for all traffic
  • CORS and security headers added manually
  • Cookies stripped from requests to enable full caching
  • Query strings removed before hitting the origin to improve cache efficiency
  • Fallback logic implemented for missing routes to support SPA behavior

Fastly VCL Configuration

Below are all the behaviors and configurations present in the final Fastly VCL configuration, reflecting both UI-driven settings and custom code implemented using VCL snippets:

Amazon S3 as origin
Configured via a custom backend pointing to the S3 REST API endpoint (setup guide).

Redirect / to /index.html

if (req.url == “/”) { set req.url = “/index.html”; }

Strip query strings from cache key

set bereq.url = regsub(bereq.url, “\?.*$”, “”);

Remove cookies before origin

unset req.http.Cookie;

Preserve CORS preflight headers

set req.http.origin = req.http.origin;
set req.http.access-control-request-method = req.http.access-control-request-method;
set req.http.access-control-request-headers = req.http.access-control-request-headers;

Add CORS headers
Set in vcl_deliver:

set resp.http.Access-Control-Allow-Origin = “*”;
set resp.http.Access-Control-Allow-Methods = “GET, HEAD, PUT, POST, PATCH, DELETE, OPTIONS“;

SPA fallback on 403/404
Implemented via restart and header flag:

# In vcl_fetch:
if (beresp.status == 403 || beresp.status == 404) {
  set req.http.X-Fallback = “true”;
  set req.url = “/index.html”;
  return (restart);
}

# In vcl_recv:
if (req.http.X-Fallback == “true”) {
  unset req.http.X-Fallback;
  set req.url = “/index.html”;
}

Signed S3 requests
Custom AWS Signature v4 logic implemented in vcl_miss using variables like var.awsAccessKey, var.signature, and header injection.

Force TLS

if (!req.http.Fastly-SSL) {
  error 801 “Force SSL”;
}

Remove AWS response headers

unset beresp.http.x-amz-id-2;
unset beresp.http.x-amz-request-id;
unset beresp.http.x-amz-delete-marker;
unset beresp.http.x-amz-version-id;

Cache settings

set beresp.ttl = 86400s;
if (beresp.ttl < 1s) {
  set beresp.ttl = 1s;
}

Enable Brotli/Gzip

if (req.http.Accept-Encoding == “br”) {
  set beresp.brotli = true;
} elsif (req.http.Accept-Encoding == “gzip”) {
  set beresp.gzip = true;
}

Strip Set-Cookie headers

if (beresp.http.Set-Cookie) {
  return (pass);
}

Shielding enabled

set req.backend = fastly.try_select_shield(ssl_shield_bfi_wa_us, F_Host_1);

Reproducing Fastly VCL Behaviors in Amazon CloudFront

Here’s how each of the VCL behaviors was replicated natively in CloudFront:

Amazon S3 as Origin

  • Go to the AWS Console and navigate to CloudFront.
  • Click Create distribution (or edit an existing one).
  • Under Origin settings:
    • Origin domain: Select your S3 bucket from the dropdown list.
    • Name: It will auto-fill, but you can customize it
    • Under Origin access, select Origin access control (OAC) settings (recommended)
    • On Origin access control, select an existing origin access control
    • If no OAC exists, click Create new OAC and follow the settings below:
      • Name: It will auto-fill, but you can customize it
      • Description: Optional
      • Signing behavior: Check Sign requests (recommended)
  • Attach OAC to the bucket: After creating the OAC and attaching it to the origin, go to the S3 bucket > Permissions > Bucket Policy, and add the policy granting access:
{
  “Version”: “2012-10-17”,
  “Statement”: [
    {
      “Effect”: “Allow”,
      “Principal”: {
        “Service”: “cloudfront.amazonaws.com”
      },
      “Action”: “s3:GetObject”,
      “Resource”: “arn:aws:s3:::YOUR_BUCKET_NAME/*”,
      “Condition”: {
        “StringEquals”: {
          “AWS:SourceArn”: “arn:aws:cloudfront::YOUR_ACCOUNT_ID:distribution/YOUR_DISTRIBUTION_ID”
        }
      }
    }
  ]
}

Replace YOUR_BUCKET_NAME, YOUR_ACCOUNT_ID, and YOUR_DISTRIBUTION_ID accordingly.

  • Leave other settings as-is, unless you have specific needs (custom headers, protocol policies, etc.).
  • Click Create Distribution or Save Changes if you’re editing.

Redirect / to /index.html

  • Open your CloudFront distribution in the AWS Console.
  • Navigate to the General tab > Settings > Edit
  • Locate the Default Root Object setting, and enter index.html
  • Save and wait for the distribution to deploy.

Strip Query Strings from cache key

  • Go to CloudFront > Policies > Cache policies.
  • Create or edit a policy:
    • Cache key settings:
      • Query strings: None
  • Save the policy
  • Attach this Cache Policy to your Behavior that handles your application entry point or dynamic routes, such as /index.html or /app/*.
    • Go to Cloudfront > your Distribution > Behaviors > Select and Edit or Create behavior
    • Cache key and origin requests:
      • Set Cache policy and origin request policy (recommended)
      • For the Cache policy, select the cache policy that you created
  • Click on Save Changes, and wait for the distribution to deploy.

Remove Cookies before Origin

Use the same steps as Strip Query Strings section, but set Cookies to None in the Origin Request Policy.

Preserve CORS preflight headers

  • Go to CloudFront > Policies > Origin request.
  • Create or edit a policy:
    • Origin requests settings:
      • In Headers, select Include the following headers
      • Select Origin, Access-Control-Request-Method, and Access-Control-Request-Headers. Need to click Add custom for each header selected. 
  • Save the policy
  • Attach this Origin requests to your Behavior.
    • Go to Cloudfront > your Distribution > Behaviors > Select and Edit or Create behavior
    • Cache key and origin requests:
      • Set Origin request policy – optional
      • On Origin policy, select the origin policy that you created
  • Click on Save Changes, and wait for the distribution to deploy.

Add CORS headers

  • Go to CloudFront > Policies > Response headers
  • Create or edit a policy:
    • Under Cross-origin resource sharing (CORS) – optional, toggle the Configure CORS, and follow the settings below:
      • Access-Control-Allow-Origin: set to All origins
      • Access-Control-Allow-Headers: set to All headers
      • Access-Control-Allow-Methods: set to All HTTP methods
      • Origin override: checked
    • Click Create
  • Attach the Policy to Your Behavior, in Response headers policy – optional
  • Save the changes

SPA fallback on 403/404

  • Go to CloudFront > your Distribution > Error pages tab
  • Click on Create custom error response
  • Select the error code in HTTP error code (403)
  • Select Yes for Customize Error Response
  • On the Response page path, put /index.html
  • Select 200 in the HTTP Response code
  • Click Create custom error response
  • Repeat the process to error 404

Signed S3 requests

Use Origin Access Control (OAC) to ensure your S3 bucket allows only signed requests through CloudFront. This process is explained above in the Amazon S3 as the Origin section.

Force TLS

  • Select your distribution in CloudFront
  • Go to the Behaviors tab, select the behavior where you want to enforce TLS, and click Edit
  • Locate “Viewer Protocol Policy”
    • Set it to:
      Redirect HTTP to HTTPS
      or
      HTTPS Only (if you want to block HTTP entirely)
  • Click “Save changes”

Remove AWS response headers

These are stripped automatically by CloudFront unless explicitly allowed — no extra setup is required.

Cache settings

Follow the same setup steps as in the Strip Query Strings section by creating a Custom Cache Policy.

  • In the policy:
    • Set Default TTL to 86400 (1 day)
    • Set Min TTL to 1 second
  • Click Create/Save changes

Enable Brotli/Gzip

Follow the same setup steps as in the Strip Query Strings section, but on Compression support, check Gzip and Brotli.

Strip Set-Cookie headers

Follow the same setup steps as in the Strip Query Strings section, but select None in Cookies.

Closing Thoughts

Migrating from Fastly to CloudFront does not imply a loss of control, particularly in scenarios involving Single Page Applications (SPAs) or static content delivery. With the evolution of CloudFront’s native features such as cache policies, response headers, and custom error handling, it is now feasible to replicate most edge behaviors without custom code.

For teams considering a similar transition, it is recommended to start by mapping existing VCL configurations to CloudFront’s policy framework. In cases where more advanced or custom logic is required, CloudFront also supports extensibility through Lambda@Edge and CloudFront Functions, providing additional flexibility when necessary.

About TrackIt

TrackIt is an international AWS cloud consulting, systems integration, and software development firm headquartered in Marina del Rey, CA.

We have built our reputation on helping media companies architect and implement cost-effective, reliable, and scalable Media & Entertainment workflows in the cloud. These include streaming and on-demand video solutions, media asset management, and archiving, incorporating the latest AI technology to build bespoke media solutions tailored to customer requirements.

Cloud-native software development is at the foundation of what we do. We specialize in Application Modernization, Containerization, Infrastructure as Code and event-driven serverless architectures by leveraging the latest AWS services. Along with our Managed Services offerings which provide 24/7 cloud infrastructure maintenance and support, we are able to provide complete solutions for the media industry.