Cloudfront Functions and Lambda@Edge Compared

Cloudfront Functions and Lambda@Edge Compared

indi-kah-tang

Kah Tang

Recently AWS released a new way to deploy node.js code to Cloudfront edge locations with a new product called Cloudfront Functions. You might wonder, where have we heard of that before. Oh right, there is already something similar called Lambda@Edge. So, why did AWS release Cloudfront Functions? And how is it different from Lambda@Edge.

Cloudfront Functions was announced May 3rd 2021 and is best described as Lambda@Edge Light, although you won’t see any Lambda for it in the Lambda dashboard. In fact, Cloudfront Functions is more integrated into Cloudfront, which is why you will find Cloudfront Functions in the Cloudfront dashboard instead.

Cloudfront functions edge location

A basic Cloudfront Function will look like this:

function handler(event) {
    var request = event.request;
    var clientIP = event.viewer.ip;
    console.log("ClientIP: " + clientIP);

    //Add the true-client-ip header to the incoming request
    request.headers['true-client-ip'] = {value: clientIP};

    return request;
}

This is a simple example that adds a True-Client-Ip header to the request, so the Origin can use it to do further processing. It’s a common scenario where your application is on an instance behind a load balancer, but you need the actual client IP for some logic.

Supported Language

First let’s look at the language, which is Javascript. At the moment this is the only supported language in Cloudfront Functions. Other languages might be supported in the future, but that is not the case for the moment. So if you want to use Python to transform your request, your only option would be Lambda@Edge.

Trigger events

Just like with Lambda@Edge you can associate a trigger event in Cloudfront with the Cloudfront Function you want to run. Where with Lambda@Edge you can have trigger events associated with 4 events (Viewer Request, Origin Request, Origin Response and Viewer Response), Cloudfront Functions is limited to Viewer Request and Viewer Response. This means you can set a trigger for every event before or after it is checked for existence in the Cloudfront cache. In most cases this is fine. But unlike Lambda@Edge, you don’t get a chance to trigger only when the request is forwarded to the Origin.
What is worthy to note is that you can have a combination of the trigger events for each behaviour in Cloudfront. So, you could for example have a Cloudfront Function on the Viewer Request, another Lambda@Edge on the Origin Request, yet another on the Origin Response and another Cloudfront Function on the Viewer Response.

cloudfront-function-and-lambda-edge-2

Feature limitations

Since Cloudfront Functions is only meant for transforming requests and responses, some access and features have been cut away to make it more scalable, performant and cost effective. The most important ones that are not available are

  • Network access. So no API’s can be called.
  • File system access. No reading or writing of files to the filesystem.
  • Timers. setTimout(), setImmediate and clearTimeout are not available.
  • Dynamic code. eval() and Functions will throw errors if you call them.
  • Date and timestamps are available. But they give the same date/time within the same call and are not usable for measuring the time of your function.

These limitations are enforced, because Cloudfront Functions are process-isolated and need to be as quick as possible. In contrast, Lambda@Edge (and Lambda) is (VM)-isolated and is a bit slower to startup.

So, if you need to do anything more than transforming the request or response, you might want to use Lambda@Edge instead.

Resource limitations

You can imagine that since Cloudfront Functions are process-isolated, it also comes with some limitations around the resources it’s allowed to consume.
Most noticeably, memory is limited to 2MB. So don’t run any memory hungry applications. And the whole deployment package of your Cloudfront Function cannot be bigger than 10kb. It will definitely be useful to use a minimizer on your code.

Deployment

This might be the most important difference. Where a Lambda@Edge is deployed to one of the 13 regional edge locations, Cloudfront Functions are deployed even further down and closer to the viewer at one of the 280+ edge locations. That is why Cloudfront Functions are more ideal for situations where you need more control over the caching headers and need to transform the request so that there is more chance for it to find a cache hit.

Speed

It doesn’t get much faster than Cloudfront Functions. While Lambda@Edge functions are limited to 5s, the maximum execution time for a Cloudfront Function is less than 1ms. This means a Cloudfront Function should not be big hit on performance.

Cost

When it comes to costs, Cloudfront Functions definitely has the edge. No pun intended. Cloudfront Functions run as low as $0.10 for 1.000.000 requests, where Lambda@Edge costs $0.20 for the same amount of requests and also adds charges per 1ms of runtime which has pricing depending on the amount of memory.

Logging and monitoring

What has been done better in Cloudfront Functions than Lambda@Edge is the monitoring. In Lambda@Edge you code deploy your code in us-east-1 and is then Cloudfront takes care of deploying it to the regional edge locations. The downside here is that all the Cloudwatch monitoring and logging are spread throughout the AWS regions and makes it a bit harder to dig into logs throughout these locations.
Cloudfront Function logs are a bit easier and can be found centrally in us-east-1 region. Any console.log() statements will log its output to Cloudwatch Logs in a loggroup called /aws/cloudfront/function/<function-name>.
Also all the monitoring can be found in us-east-1.

Conclusion

So, when should you use Cloudfront Functions over Lambda@Edge? As mentioned, Cloudfront Functions is meant for transformations. For example, you might want to redirect the viewer to a different cache, based on country. Or you would like to have more control over the Cache-Control header, based on the resource that is being requested.

In other cases where you would like to request some API’s first or retrieve files from S3, such as for adding a watermark, Lambda@Edge will seem like the better option here.

In case of doubt, try Cloudfront Functions first and migrate to Lambda@Edge when the need arises.