CloudFront and Load Balancer with Host-Based Routing

This post is about using CloudFront with a single Load Balancer origin to route traffic to multiple different targets. In my previous post, I described how to use a single S3 bucket/CloudFront pair to host multiple websites. At some point, I needed to add a dedicated backend to each of my static websites, which led to the creation of this post. However, this configuration is not limited to that specific use case.

Theory

AWS Application Load Balancer can route traffic to targets based on the Host header in the HTTP request, a process known as host-based routing. This makes it possible to use a single ALB to serve multiple websites and APIs.

If you have a DNS record that points to the ALB directly, e.g:

CNAME aaa.example.com -> my-alb-2531886456.us-east-1.elb.amazonaws.com

then the aaa.example.com Host header will be passed to the ALB, allowing host-based routing to work seamlessly.

However, if you use CloudFront, your DNS record will be:

CNAME aaa.example.com -> xxxxx.cloudfront.net

and CloudFront will be responsible for routing the traffic to your ALB. By default, CloudFront doesn’t include the Host header in its request to the ALB, preventing host-based routing from working. To resolve this, you must add the Host header to the CloudFront cache key.

Practice

First, you must have an ALB with host-based routing configured. If you do not have one yet, refer to this short step-by-step guide from AWS.

Second, add your ALB as a custom origin in CloudFront by selecting your CloudFront distribution, then OriginsCreate origin. Choose your ALB as the Origin domain and provide a Name; the other fields can keep their default values.

Third, create a Cache Behavior for your ALB origin and specify that the cache key should include the Host header:

/posts/cloudfront-loadbalancer-host-based-routing/cache-behavior.webp
Including Host header
You can choose Legacy cache settings as shown on the screenshot, or you can also choose the Cache policy and origin request policy option and create a custom cache policy that will include the Host header. For the sake of brevity, I’m going with the Legacy option here.

With these steps completed, each hostname will have its own dedicated backend, thanks to the ALB host-based routing and the Host header passed from CloudFront to the ALB:

aaa.example.com -> xxxxx.cloudfront.net ->               -> Target A
bbb.example.com -> xxxxx.cloudfront.net ->  ALB Origin   -> Target B
ccc.example.com -> xxxxx.cloudfront.net ->               -> Target C