Networking & Content Delivery

Cost-Optimizing your AWS architectures by utilizing Amazon CloudFront features

Amazon CloudFront is a global content delivery network (CDN) that makes it easy to deliver websites, videos, apps, and APIs securely and at high speeds with low latency. You can use CloudFront to reduce latency by delivering data through 400+ globally dispersed Points of Presence (PoPs) and improve security with traffic encryption, access controls, and resiliency against DDoS attacks. In addition to performance and security, CloudFront can be used to cost optimize your AWS infrastructure in various ways. In this post, we’ll cover numerous CloudFront features and best practices that can help optimize costs for some commonly deployed architectures.

Use CloudFront for everything – including dynamic content

Suppose you serve dynamic content via web applications or APIs hosted directly from an Elastic Load Balancer (ELB) Amazon Elastic Compute Cloud (Amazon EC2) instances, or Amazon Elastic Container Service (Amazon ECS)/Amazon Elastic Kubernetes Service (Amazon EKS) container cluster to end users on the Internet. Instead of serving the requested content directly from these resources, you can route this traffic via CloudFront, configured to pass-through the content without caching it at the edge locations. This approach lets you utilize CloudFront’s Free Tier, which offers 1 TB of data transfer out to the internet, and 10 million HTTP or HTTPS requests free each month, thereby reducing your Data Transfer Out (DTO) costs.

If you are using a third-party CDN while hosting your applications on AWS, you should be aware that this can be an anti-pattern from a cost perspective of data transfer. Let’s understand why – assume that you’re running a commonly-deployed web application behind Application Load Balancers (ALB) on AWS and using a different CDN instead of CloudFront. Now assume that your application is serving 10 TB of data to your users per month. If you use CloudFront as your CDN, then you’ll pay a data-transfer cost for only 9 TB because CloudFront Free Tier will cover the first 1 TB every month. Additionally, there is no data transfer cost for the data that is transferred between your origin servers on AWS, such as ALB, AWS Elastic Beanstalk, Amazon Simple Storage Service (Amazon S3), to the edge locations. However, if you use a different CDN, you’ll pay data transfer costs for 10 TB to them as per their pricing plans, as well as incur an additional data transfer cost to AWS for your data that is transferred out from your origin servers on AWS to the edge locations of the other CDN.

Architecture showing data transfer with and without Cloudfront

Figure 1: Data flow architecture for content delivery network

Restrict serving content to unwanted regions using CloudFront geographic restriction

Unwanted and malicious traffic can result in additional load on your resources, consume bandwidth, and increase your AWS costs. Restricting unwanted traffic at the edge before it hits your other resources can help you save on costs. For example, suppose you don’t want traffic from specific countries to hit your applications. In that case, you can use the CloudFront geographic restrictions feature to restrict access to all of the files associated with a CloudFront distribution at the country level. This can reduce the amount of traffic that your origin servers must process. Therefore, you can scale down your origin resources appropriately, resulting in cost savings.

Use CloudFront price classes as a metric for edge location strategy

CloudFront has edge locations all over the world. The cost for each edge location varies, and thus the price varies depending on which edge location serves the requests. Price classes let you reduce your delivery prices by excluding CloudFront’s more expensive edge locations from your CloudFront distribution. Therefore, configuring the price class basis of application user geographies helps reduce costs without causing any noticeable latency impact.

Screenshot of AWS console showing settings to select all edge locations

Figure 2: CloudFront price class options

Implement controls to reduce the cost of CloudFront logging

You may want to monitor your content distribution metrics, which you can get using CloudFront logs. However, note that real-time logs are charged based on the number of generated log lines, which in turn is based on the number of HTTP requests served by your CloudFront distribution. Therefore, as a best practice, we recommend putting some preventive controls in place to limit the amount of traffic served by your CloudFront distribution using security services such as AWS Web Application Firewall (AWS WAF). You can also limit the amount of logs generated in three ways:

  1.  Specify what percentage of requests you want to log
  2. Choose to log only specific log fields
  3. Enable real-time logs only for specific CloudFront caching behaviors

You can configure all of this on the CloudFront logs settings as shown in the following image.

Screenshot of AWS console showing Cloudfront logs settings

Figure 3: CloudFront logging

Optimize cache hit ratio for non-AWS Origins

CloudFront is often used as a content distribution layer for applications hosted outside of AWS. In these cases, optimizing the cache hit ratio can help save origin request submission costs. Cache hit ratio is the percentage of total requests that are served from the content cached at the edge locations. Take advantage of CloudFront’s customizable cache policies to improve the cache hit ratio by controlling the cache key. The cache key is the unique identifier for every object in the cache, and it determines whether a viewer request results in a cache hit. A cache hit occurs when a viewer request generates the same cache key as a prior request, and the object for that cache key is in the edge location’s cache and valid. One way to improve your cache hit ratio is to include only the minimum necessary values in the cache key. Optimize your cache hit ratio by understanding the cache key. In addition, CloudFront provides some predefined cache policies, known as managed policies, for common use cases or supports creating a custom cache policy specific to application requirements.

Screenshot of AWS console showing cache key settings

Figure 4: CloudFront cache key settings

Effectively utilize CloudFront compressed data caching capabilities

CloudFront natively supports requesting and caching objects compressed in the GZIP or Brotli compression formats. CloudFront serves the compressed objects when the viewer’s web browsers or other clients support them and indicate their support for compressed objects with the Accept-Encoding HTTP header. Object compression helps reduce your costs because you’re transferring less data out from your origin servers to the internet. If certain file types aren’t natively supported for compression at CloudFront, then you can compress these files at your origin and return them as a compressed object to CloudFront. CloudFront detects that the object is compressed based on the presence of a Content-Encoding header and won’t compress the object again.

Screenshot of AWS console showing default cache behavior compression settings

Figure 5: CloudFront compressed data cache

Optimize CloudFront caching strategy to reduce cache invalidations

If you must remove a file from CloudFront edge caches before it expires, then you can invalidate the file from edge caches. You’re charged for invalidation requests beyond the first 1,000 paths requested for invalidation each month. Therefore, it’s important to know some general best practices to save costs on invalidation.

We recommend that you control caching using the Cache-Control HTTP header that is sent by your origin, where you can define how long an object is cached by setting a time to live (TTL). CloudFront honors Cache-Control headers. This means that if an object has a Cache-Control header in the response, then CloudFront caches the object at the edge location for the duration of time specified in the Cache-Control max-age directive. If there is no Cache-Control header, then CloudFront caches the object for the duration of the Default TTL specified in the applicable cache behavior. In addition, you can define boundaries on the least and the most amount of time any object is cached by CloudFront by using the Minimum TTL and Maximum TTL fields of the cache behavior to protect against excessively short or long TTLs from being set unintentionally.

Screenshot of AWS console showing Cloudfront TTL settings

Figure 6: CloudFront TTL options

If you must remove a file from CloudFront edge caches before it expires, then you can consider using file versioning to serve a different version of the file that has a different name. Versioning enables you to control which file a request returns even when the user has a version cached either locally or behind a corporate caching proxy. If you invalidate the file, then the user might continue to see the old version until it expires from those caches. Versioning is less expensive. You still have to pay for CloudFront to transfer new versions of your files to edge locations in the case of non-AWS origins, but you don’t have to pay for invalidating files. For more information, see Updating existing files using versioned file names.

Consider using wildcard (*) path invalidations instead of individual file invalidations. You pay for each invalidation path, however an invalidation path can be for a single file, such as (/images/logo.jpg) or multiple files (such as /images/*). A path that includes the * wildcard counts as one path even if it causes CloudFront to invalidate thousands of files.

Optimize Lambda@Edge execution costs by rightsizing AWS Lambda runtime

The cost of running Lambda@Edge for your workload is determined by three factors: the number of executions, the duration of execution of the AWS Lambda function, and the memory usage (combined as Gb/s). Your choice of Lambda runtime has a direct impact on the cost. Generally, compiled languages run code more quickly than interpreted languages, but they can take longer to initialize. For small functions with simple functionality, often an interpreted language is better suited for the fastest total execution time, and thus the lowest cost. Functions using compiled languages are often faster in workloads with heavier computational complexity or where you’re using Provisioned Concurrency, so the initialization overhead occurs before the invocation.

Conclusion

This post explains some best practices that can help you optimize your costs for some commonly deployed architectures on AWS by leveraging Amazon CloudFront. Use these features in conjunction with other features that can help you improve the performance of web applications using CloudFront.

Abhishek Sarolia

Abhishek is a Solutions Architect with AWS. He works with AWS customers to address their business challenges by designing secure, performant, and scalable solutions leveraging the latest cloud technologies. He is passionate about technology and enjoys building and experimenting with AI/ML and IoT. Outside of work, he enjoys traveling, reading non-fiction books, and spending time with his family.

Karan Desai Headshot

Karan Desai

Karan is a Solutions Architect at AWS in India. He has over eleven years of cloud industry experience, with a focus on edge technologies and networking. He currently works with companies who are in their early phases of cloud adoption, designing scalable cost-efficient solutions that empower them to modernize and grow using AWS. Outside of work, he can be found traveling to offbeat places and trying out unusual foods.