Bite-Sized Serverless

CloudFront Icon

The Anatomy of a CloudFront Distribution

CloudFront - Intermediate (200)
CloudFront is a Content Distribution Network (CDN). The basic functionality of a CDN is to store a copy of cacheable assets near your users. This reduces latency and reduces the load on the backend server, which in the context of CloudFront is called the origin.
At the time of writing the CloudFront network has 218 points-of-presence (POPs). When a user connects to the hostname for your distribution (e.g. ex4mpl3d3m0.cloudfront.net), DNS routes the traffic to the nearest POP enabled for your content. Which POPs are enabled depends on the price class the distribution has been configured with: price class 100 includes North America and Europe, price class 200 also includes South Africa, the Middle East, Japan and Asia, and price class 'all' adds the rest of the world, including South America and Australia. When you have selected price class 100 or 200, a request from a non-included region will have to travel to a POP further away, which means it has higher latency.
At this point, you might think of CloudFront as a single layer of POPs in front of your origin. A request is routed to the POP nearest to the user. If the POP has the object, it returns it to the user. If it doesn't, it fetches it at the origin and stores it in the cache for the next user.

Adding regional edge locations to the mix

POPs only have limited cache sizes, which are also shared with every other CloudFront distribution. This means that objects which aren't accessed very often might get evicted (removed from the cache location) to make room for newer objects. If an object is requested after it has been evicted, the POP has to retrieve the file from the origin again. If this happens too often, the added value of a CDN would be very limited.
To improve the cache hit ratio (the amount of times an object can be served from cache), CloudFront adds another layer of caches between the POPs and the origin. These regional edge caches have much large cache sizes and can retain objects for a longer period of time. As the name suggests, the regional edge caches are located in the same AWS regions you're familiar with from other services. However, not every AWS region hosts a CloudFront regional edge location: at the time of writing there are 13 regional edge caches.
When a POP does not have a requested asset in its local cache, it always forwards the request to its regional edge location. Because multiple POPs share the same regional edge cache, it has a higher chance to have the object than any individual POP. If the regional edge cache indeed has the object available, it is immediately returned to the POP. If the regional edge cache does not have the object it is fetched from the origin, and stored in both the regional edge location and the POP for subsequent requests.
Regional edge caches complicate CloudFront's anatomy somewhat. A POP will always forward a request for a cacheable resource to its regional edge cache. Proxy methods (PUT / POST / PATCH / OPTIONS / DELETE) and dynamic requests, however, are forwarded directly to the origin, as indicated in the diagram below.

Lambda@Edge and CloudFront functions

Lambda@Edge functions were announced at Re:Invent 2016. They modify inbound requests or outbound responses using lightweight code written in Node.js or Python. Lambda@Edge functions can be configured to execute in four different stages of a CloudFront request:
  1. When CloudFront receives a request from a viewer (viewer request)
  2. Before CloudFront forwards a request to the origin (origin request)
  3. When CloudFront receives a response from the origin (origin response)
  4. Before CloudFront returns the response to the viewer (viewer response)
These four stages are indicated in the diagram below.
Lambda@Edge functions configured for viewer request (1) and viewer response (4) will be executed for every single request, respectively just before they hit the POP and just after they leave the POP. These functions are commonly used to categorize traffic before it hits the cache, to modify the cache key (see the Bite Cache Control with CloudFront Functions), or to add or remove headers from the request or response.
The origin request (2) and origin response (3) functions will only be executed when an object was not found in the cache and has to be retrieved at the origin. This function can be used to authenticate CloudFront with the origin, or to add default headers to any origin response. The values returned by the origin response will be stored in the cache.

CloudFront functions

CloudFront functions have a lot of overlap with Lambda@Edge functions. They were introduced a much more recently, in May 2021. CloudFront functions can only be deployed as viewer request (1) and viewer response (4) functions. They are deployed at the CloudFront points-of-presence, so they always run very close to your users. CloudFront functions have a significantly reduced feature set compared to Lambda@Edge functions and can only be written in ECMAScript 5.1 compliant JavaScript. These functions execute very quickly, even within a single millisecond. CloudFront functions are great for header manipulation, cache key manipulation and generating 3xx or 4xx responses before a request even hits the POP.
Examples of CloudFront functions in action can be found in the Bite Cache Control with CloudFront Functions. This Bite also contains a CDK project which allows you to quickly deploy and tinker with CloudFront functions yourself.

Origin Shield

The last component of the CloudFront anatomy is CloudFront Origin Shield, although 'component' might be a misnomer - 'reconfiguration' might be a better description. You use CloudFront Origin Shield when the application serving as CloudFront's origin is very sensitive to peak loads, or if a new release (such as an article posted to social media) could lead to sudden large spikes.
When you enable Origin Shield, you select a regional edge location (such as eu-west-1) as the Origin Shield region. This will reconfigure every other regional edge location to route their requests to the Origin Shield location, instead of directly to your origin. This helps protect your origin in three ways:
  1. The Origin Shield edge location is very likely to have requested assets in its cache. This improves the cache hit ratio.
  2. When users all over the world request a new resource, their requests are routed to the Origin Shield instead of (potentially) overloading your origin.
  3. Origin Shield bundles simultaneous requests for the same resource into a single request for your origin.
Additionally, all traffic between edge locations uses the high speed, high throughput CloudFront global backbone.
This design has some impact on the region running your Lambda@Edge functions, as displayed in the diagram above. Because Lambda@Edge functions sit between the regional edge caches and the origin, they are generally hosted in all regions. With Lambda@Edge, there is only one regional edge location connecting to your origin, so Lambda@Edge will only execute in your Origin Shield region.

Conclusion

In this Bite we have seen that there is more to CloudFront infrastructure than initially meets the eye. We have covered the two layers of edge caches, the two types of functions used to manipulate requests, and the Origin Shield configuration which further reduces load on your origin. Understanding this architecture will help you improve your solution designs, and help you to better understand potential bottlenecks.