Optimising Content Delivery with AWS Lambda@Edge and Cache

Tech Community • 10 min read

In today's digital world, where speed and efficiency are key, optimising content delivery is a critical aspect of ensuring a seamless user experience. The lambda edge functions offer a powerful solution for achieving this goal by enabling software engineers to run code closer to end-users, reducing latency and improving performance. 

When coupled with caching mechanisms, the lambda functions become even more potent, allowing for dynamic content delivery with enhanced speed and scalability. In this post, we'll dive into how lambda edge and cache work together to optimise content delivery on AWS.

The lambda edge functions are an extension of the lambda functions that allow you to run code in response to CloudFront events, which occur as content is requested by users. This enables you to execute custom logic at AWS edge locations, closer to end-users, rather than at centralised servers. By doing so, lambda edge minimises latency and increases the responsiveness of web applications, APIs, and dynamic content delivery.

Fusing functions

The fusion of lambda edge functions and CloudFront's advanced cache policies presents a great opportunity to revolutionise content delivery strategies. By strategically deploying cache mechanisms at edge locations, in close geographical proximity to end-users, we can significantly reduce response times for services accessed worldwide. 

This geographical optimisation enhances user experience by minimising latency and offers a customisable approach to cache management, enabling us to dynamically adjust caching strategies based on user location and demand patterns. In essence, this synergy between lambda edge and CloudFront unlocks a new frontier in content delivery optimisation, where speed, efficiency, and scalability converge to redefine the digital experience for users across the globe.

Caching plays a crucial role in optimising content delivery by storing frequently accessed data closer to end-users, thereby reducing the need for repeated requests to the origin server. When used in conjunction with lambda edge, caching becomes even more impactful, as it allows you to cache dynamic content generated by Lambda functions at edge locations.

It's known that CloudFront has enhanced its capability to implement policies for modifying cache behaviour. Consequently, AWS has chosen to separate the modification of response headers from serverless environments like lambda edge functions or CloudFront Functions. As a result, CloudFront will now include the following headers in the traffic passing through it: Cache-Control: max-age, stale-while-revalidate, stale-if-error.

Benefits of this approach

However, the use of lambda edge functions alongside CloudFront continues to yield numerous advantages. In addition to the typical advantages of Lambda functions such as "infinite" scalability and their code-first approach, we have other benefits that we can leverage when using this combination of technologies. 

Custom Cache-Control: Lambda edge functions allow you to customise cache control directives based on specific criteria such as request headers, query parameters, or response headers. 

This granular control enables you to finely adjust caching behaviour and enhance cache efficiency for various content types or user interactions, aligning perfectly with CloudFront configurations.

Dynamic Content Caching: While CloudFront's cache primarily serves static or immutable content, lambda edge can generate and cache dynamic content at the edge in real-time. This means you can cache personalised or dynamically generated responses, reducing latency and improving responsiveness for dynamic content delivery. 

This capability, when combined with services like Redis or ElastiCache, creates a significant advantage, allowing for even greater flexibility and efficiency in managing dynamic content caching at the edge.

Cache Invalidation Logic: Lambda Edge enables you to implement custom cache invalidation logic based on events or triggers. Allowing us to invalidate cache entries selectively in response to content updates, ensuring that users always receive the latest information without stale data issues. Instead of forcing the user to wait until the TTL of the content being served to them expires to obtain the new version, we take control and define when we want to change the content stored in the cache.

Content Transformation: The lambda edge functions can perform on-the-fly content transformation or manipulation before caching or serving responses. This includes tasks such as image resizing, HTML minification, or compression, and optimising content for better performance and bandwidth efficiency.

Using AWS Lambda@Edge and Cache: A Technical Perspective

Let's delve into a use case: envision a video-on-demand (VOD) provider for which ensuring an optimal user experience is crucial. From minimising loading times to delivering personalised content customised to diverse global audiences, the demands on such platforms are substantial. We will explore how the integration of AWS Lambda@Edge and cache mechanisms can significantly enhance the performance and customisation capabilities of VOD platforms.

Use Case Scenario

Imagine a scenario where a video-on-demand provider seeks to optimise its streaming service. With a vast user base all around the globe, the platform must efficiently handle user requests while delivering tailored content based on individual preferences and geographical locations.

Streamlining Request Handling with Lambda@Edge

Lambda@Edge presents a compelling solution to alleviate the burden on servers hosting APIs. As request rates escalate, traditional scaling approaches may become inadequate. However, Lambda@Edge offers a more agile alternative, allowing for the offloading of certain processing tasks from centralised servers to edge locations closer to end-users.

By deploying Lambda@Edge functions strategically, a VOD platform can distribute request handling across a network of edge locations, thereby reducing latency and improving responsiveness. This distributed approach not only enhances scalability but also mitigates the risk of server overload during peak usage periods such as during major events like the Bundesliga or the Olympic games or, even better, several of them happening at the same time.

Enhancing Content Personalisation through Cache Optimisation

One of the key challenges in VOD services is delivering personalised content efficiently. Without effective cache utilisation, each request may entail time-consuming interactions with backend servers to retrieve relevant metadata and content. This process, while essential for customisation, can significantly impact response times and user experience.

By implementing cache mechanisms, such as caching together with Lambda@edge, a VOD platform can store frequently accessed metadata and content closer to end-users. This cached data can include region-specific information, user preferences, and even pre-rendered video segments. As a result, subsequent requests for the same content or metadata can be fulfilled directly from the cache, minimising backend server interactions and accelerating content delivery.

Geolocation and Metadata Utilisation with Lambda@Edge

Lambda@Edge functions also offer powerful capabilities for leveraging geolocation data and cached metadata to further enhance content personalisation. By analysing the geographic location of user requests, Lambda@Edge functions can dynamically adjust content delivery based on regional preferences, language settings, and licensing restrictions.

For example, users in different regions may have access to distinct content libraries due to licensing agreements or regional preferences. Lambda@Edge functions can intelligently route requests to the appropriate content repositories based on the user's location, ensuring compliance with licensing terms and delivering a personalised viewing experience.

The integration of AWS Lambda@Edge and cache optimisation techniques offers a compelling solution for enhancing the performance and customisation capabilities of digital services, including video-on-demand platforms. By leveraging edge computing, cache mechanisms, and geolocation data, this combination enables seamless content delivery tailored to individual user preferences and regional requirements, optimising responsiveness, scalability, and cost-effectiveness across various digital platforms.

Adding Terraform Files for Creating the CDN and Managing Lambda@Edge Function

In this section, I'll walk through using Terraform to create a CDN with AWS CloudFront and configure Lambda@Edge for country-based blocking and redirection. I'll provide the main.tf and lambda.tf files, with placeholders where you can insert your specific configurations.

Prerequisites

Before proceeding, ensure you have:

  • An AWS account with necessary permissions
  • Terraform installed on your local machine
  • AWS CLI configured with your credentials

Step 1: Define the Terraform Configuration

Create a directory for your Terraform files, and within this directory, create the following files:

  1. main.tf
  2. lambda.tf
  3. lambda/origin_request.py

1. main.tf

This file contains the main configuration for the CloudFront distribution. We'll specify that only clients from the US, CA, GB, and DE are allowed access to the CDN.

provider "aws" {
 region  = "us-east-1"
}




resource "aws_cloudfront_distribution" "cdn" {


 restrictions {
   geo_restriction {
     restriction_type = "whitelist"
     locations        = ["US", "CA", "GB", "DE"]
   }
 }


 origin {
   domain_name = "your_custom_origin"
   origin_id   = "your_custom_origin_name"


   custom_origin_config {
     http_port              = 80
     https_port             = 443
     origin_protocol_policy = "http-only"
     origin_ssl_protocols   = ["TLSv1.2", "TLSv1.1", "TLSv1"]
   }
 }
 default_cache_behavior {
   allowed_methods = ["GET", "HEAD"]
   cached_methods  = ["GET", "HEAD"]
   target_origin_id = "your_custom_origin_name"


   forwarded_values {


     cookies {
       forward = "none"
     }
     query_string = true
     headers      = ["Accept-Language", "Referer", "CloudFront-Viewer-Country", "User-Agent"]
   }


   viewer_protocol_policy = "redirect-to-https"


   lambda_function_association {
     event_type   = "origin-request"
     lambda_arn   = aws_lambda_function.origin_request_lambda.qualified_arn
     include_body = false
   }
 }


 enabled = true


 tags = {
   Blog = "post"
 }


 price_class = "PriceClass_All"




 viewer_certificate {
   cloudfront_default_certificate = true
 }
}


output "cloudfront_distribution_id" {
 value = aws_cloudfront_distribution.cdn.id

2. lambda.tf 



locals {
 origin_request_lambda = "origin_request_lambda"
}


resource "aws_iam_role" "lambda_role" {


 assume_role_policy = <<EOF
{
 "Version": "2012-10-17",
 "Statement": [
   {
     "Action": "sts:AssumeRole",
     "Principal": {
       "Service": [
           "lambda.amazonaws.com",
           "edgelambda.amazonaws.com"
       ]
     },
     "Effect": "Allow",
     "Sid": ""
   }
 ]
}
EOF


}


resource "aws_cloudwatch_log_group" "lambda_log_group" {
 name              = "/aws/lambda/us-east-1.lambda_log_group"
 retention_in_days = 14
 tags = {
   Blog = "post"
 }
}


resource "aws_iam_policy" "lambda_logging" {
 path        = "/"
 description = "IAM policy for logging from a lambda"


 policy = <<EOF
{
 "Version": "2012-10-17",
 "Statement": [
   {
     "Action": [
       "logs:CreateLogGroup",
       "logs:CreateLogStream",
       "logs:PutLogEvents"
     ],
     "Resource": "arn:aws:logs:*:*:*",
     "Effect": "Allow"
   }
 ]
}
EOF
}


resource "aws_iam_role_policy_attachment" "lambda_logs" {
 policy_arn = aws_iam_policy.lambda_logging.arn
 role       = aws_iam_role.lambda_role.name
}


data "archive_file" "origin_request_lambda" {
 type        = "zip"
 source_file = "./lambda/origin_request.py"
 output_path = "origin_request_lambda.zip"
}


resource "aws_lambda_function" "origin_request_lambda" {
 depends_on    = [aws_iam_role_policy_attachment.lambda_logs]
 filename      = data.archive_file.origin_request_lambda.output_path
 function_name = local.origin_request_lambda
 role          = aws_iam_role.lambda_role.arn
 handler       = "origin_request.handler"


 source_code_hash = filebase64sha256(data.archive_file.origin_request_lambda.output_path)


 runtime = "python3.12"
 publish = true
 tags = {
   Blog = "post"
 }
}

3. lambda/origin_request.py.

Additionally, we'll configure the Lambda function to redirect users from the US, CA, and DE to specific URLs.

from urllib.parse import parse_qs, urlencode
import logging
logger = logging.getLogger()
logger.setLevel("INFO")


def handler(event, context):


   url = 'https://example.com/'


   custom_domainname = event.get('Records', [{}])[0].get('cf', {}).get('request', {}).get('origin', {}).get('custom', {}).get('domainName')
   headers = event.get('Records', [{}])[0].get('cf', {}).get('request', {}).get('headers', {})
   viewer_country = headers.get('cloudfront-viewer-country', [{}])[0].get('value',None)


   if viewer_country:
       if viewer_country == 'CA':
           url = 'https://ca.example.com/'
       elif viewer_country == 'US':
           url = 'https://us.example.com/'
       elif viewer_country == 'DE':
           url = custom_domainname
   logger.info(f'Redirecting to: {url}')
   response = {
       'status': '301',
       'headers': {
           'location': [{
               'key': 'Location',
               'value': url
           }]
       }
   }


   return response

Step 2: Initialise and Apply the Terraform Configuration

Run the following commands in your Terraform project directory to deploy the infrastructure:

terraform init

terraform apply

This will set up your CloudFront distribution with the Lambda@Edge function to handle country-based blocking and redirection. It will also package your Lambda function code into a ZIP file and update the Lambda function with it.

Conclusions

In conclusion, leveraging Terraform to deploy and manage our CDN infrastructure with AWS CloudFront and Lambda@Edge provides full control over content delivery and allows efficient customisation based on the user's country of origin. 

This setup harnesses the benefits of low-latency, high-availability CDN services while easily extending functionality with additional Lambda@Edge behaviours, such as response manipulation or custom authentication, using consistent code and streamlined deployment processes. This approach simplifies management and enhances scalability for web applications and distributed content services on AWS.

Javier Pinilla GarciaLinkedInCloud Engineer

Get in Touch.

Let’s discuss how we can help with your cloud journey. Our experts are standing by to talk about your migration, modernisation, development and skills challenges.

Ilja Summala
Ilja’s passion and tech knowledge help customers transform how they manage infrastructure and develop apps in cloud.
Ilja Summala LinkedIn
Group CTO