5

I have an ELB that I want to put behind CloudFront. Let's say the ELB is example.us-east-1.elb.amazonaws.com. It only accepts requests with the Host header set to www.example.com, which I now want to host with CloudFront. Is it possible to tell CloudFront to use this Host header? When I try to set the Host header in the AWS console for CloudFront, I receive the error message com.amazonaws.services.cloudfront.model.InvalidArgumentException: The parameter HeaderName : Host is not allowed. (Service: AmazonCloudFront; Status Code: 400; Error Code: InvalidArgument; Request ID: dead-beef-badc0ffee1).

Carl
  • 153
  • 1
  • 1
  • 4
  • 2
    Can you update DNS to have either an `A` record or a `CNAME` record? In that case you wouldn't have to hack this together. – stdunbar Dec 18 '17 at 17:13
  • How would changing DNS help? In DNS, www.example.com will point to CloudFront. Even if I add origin.example.com as a DNS alias for example.us-east-1.elb.amazonaws.com, the ELB will still be getting the "wrong" host name from CloudFront. – Carl Dec 19 '17 at 19:19
  • My thought is that the HTTP `Host:` header will be passed through to the backend. – stdunbar Dec 19 '17 at 19:58

1 Answers1

11

Presumably, if the service on the ELB only answers to www.example.com then that's the hostname you're going to be pointing to CloudFront -- so, your solution is straightforward: in the Cache Behavior settings, whitelist the Host header for forwarding to the origin.

In this configuration, CloudFront passes through the Host header sent by the browser, which must be added to the list of Alternate Domain Names in the distribution's configuration. Requests for dzzzexample.cloudfront.net will fail, because your origin won't understand them, but that's usually good, because you don't want to have search engines indexing your content under the CDN domain name.

However, that might not be your plan. If that configuration won't work for your application, you need a Lambda@Edge Origin Request trigger to modify the Host header.

'use strict';

// force a specific Host header to be sent to the origin

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    request.headers.host[0].value = 'www.example.com';
    return callback(null, request);
};

Note that the Host header is immutable in an Origin Request trigger unless you configure the Cache Behavior to whitelist the Host header as described above. In this case, you're whitelisting the Host header set by the Lambda@Edge trigger, rather than the one from the browser, but the CloudFront configuration is the same.

You can't use Host in the static Custom Origin Headers configuration in CloudFront -- that's not a supported configuration. The Lambda trigger has the same effect that setting would have, if it were permitted.

Michael - sqlbot
  • 21,988
  • 1
  • 57
  • 81
  • "Presumably, if the service on the ELB only answers to www.example.com then that's the hostname you're going to be pointing to CloudFront" No, the idea is that the DNS will be changed so that www.example.com points to Cloudfront. So, you can't enter www.example.com into cloudfront as the origin address, it would just be circular. It sounds like Lambda@Edge is the only solution. – Carl Dec 19 '17 at 19:15
  • @Carl *"No, the idea is that the DNS will be changed so that www.example.com points to Cloudfront."* Yes, which is exactly the same thing I intended: that's the hostname you'll be pointing to CloudFront (in DNS). CloudFront by default sends the configured origin host name (which will be something else) as the `Host` header, but if you whitelist the Host header, then the hostname pointed to CloudFront and requested by the browser will be what is sent to the origin. – Michael - sqlbot Dec 19 '17 at 20:52
  • 1
    FYI, modifying the Host header via Lambda@Edge doesn't work and results in the following error: _"The Lambda function result failed validation: The function tried to add, delete, or change a read-only header."_ – Chad Johnson Mar 16 '22 at 06:25