Search code examples
node.jsaws-lambdaaws-api-gatewayamazon-cloudfront

passing cloud front custom domain URL to lambda


I have a custom domain URL (my-custom-domain.com), and the REST API supports query and path parameters.

https://my-custom-domain.com/hello

https://my-custom-domain.com?firstparam=abc&secondparam=def

The invoked lambda has to return the response with some path/query parameters appended to the custom domain URL in json body. Basically the other resources which can be accessed.

Example: https://my-custom-domain.com/hellofromlambda1123

https://my-custom-domain.com?firstparam=abc&secondparam=yourblogpage&pagenumber=30

An Ideal usecase is pagination, where I have to give the previous and next links. How do I pass the custom domain URL to my lambda. I am working on node js 8

In conventional JAVA programming we can achieve this by HttpServletRequest.getRequestURL(). What is the way to get the custom Domain URL. I have enabled Headers for DefaultCacheBehavior. The host in the lambda event gives the API gateway URL. Is there a way to get the mapping of the Custom Domain inside lambda?

My Cloud Formation Template for custom domain looks like this

AWSTemplateFormatVersion: '2010-09-09'
Description: Custom domain template

Parameters:
  ServiceName:
    Description: Name of the Service
    Type: String
  DeploymentEnv:
    Default: dev
    Description: The environment this stack is being deployed to.
    Type: String
  CertificateId:
    Description: SSL Certificate Id
    Type: String
  DomainName:
    Description: Name of the custom domain
    Type: String
  HostedZoneId:
    Description: Id of the hosted zone
    Type: String

Resources:
  APIDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Origins:
        - DomainName:  
            Fn::ImportValue:
              !Sub "InvokeURL-${DeploymentEnv}"
          Id: !Sub 'Custom-Domain-${DeploymentEnv}'
          CustomOriginConfig:
            OriginProtocolPolicy: https-only
            OriginSSLProtocols: [TLSv1.2]
        Enabled: 'true'
        DefaultCacheBehavior:
          AllowedMethods:
          - DELETE
          - GET
          - HEAD
          - OPTIONS
          - PATCH
          - POST
          - PUT
          DefaultTTL: 0
          TargetOriginId: !Sub 'Custom-Domain-${DeploymentEnv}'
          ForwardedValues:
            QueryString: 'true'
            Cookies:
              Forward: none
            Headers:
              - 'Accept'
              - 'api-version'
              - 'Authorization'
          ViewerProtocolPolicy: https-only
        Aliases:
          - !Sub '${DomainName}'
        ViewerCertificate:
          AcmCertificateArn: !Sub '${CertificateId}'
          SslSupportMethod: sni-only
          MinimumProtocolVersion: TLSv1.2_2018
  APIDNSRecord:
    Type: AWS::Route53::RecordSet
    DependsOn: "APIDistribution"
    Properties:
      HostedZoneId: !Sub '${HostedZoneId}'
      Comment: DNS name for the custom distribution.
      Name: !Sub '${DomainName}'
      Type: A
      AliasTarget:
        DNSName: !GetAtt APIDistribution.DomainName
        HostedZoneId: Z2FDTNDATAQYW2
        EvaluateTargetHealth: false
Outputs:
  DomainName: 
    Value: !GetAtt APIDistribution.DomainName

Solution

  • Thanks to @thomasmichaelwallace for pointing to my post on the AWS Forum that explains a way to inject the original request Host header into an alternate request header, using a Lambda@Edge Origin Request trigger. That is one solution, but requires the Lambda trigger, so there is additional overhead and cost. That solution was really about a CloudFront distribution that handles multiple domain names, but needs to send a single Host header to the back-end application while alerting the application of another request header, which I arbitrarily called X-Forwarded-Host.

    There are alternatives.

    If the CloudFront distribution is only handling one incoming hostname, you could simply configure a static custom origin header. These are injected unconditionally into requests by CloudFront (and if the original requester sets such a header, it is dropped before the configured header is injected). Set X-Forwarded-Host: api.example.com and it will be injected into all requests and visible at API Gateway.

    That is the simplest solution and based on what's in the question, it should work.

    But the intuitive solution does not work -- you can't simply whitelist the Host header for forwarding to the origin, because that isn't what API Gateway is expecting.

    But there should be a way to make it expect that header.

    The following is based on a number of observations that are accurate, independently, but I have not tested them all together. Here's the idea:

    • use a Regional API Gateway deployment, not Edge-Optimized. You don't want an edge-optimized deployment anyway when you are using your own CloudFront distribution because this increases latency by sending the request through the CloudFront network redundantly. It also won't work in this setup.
    • configure your API as a custom domain (for your exposed domain)
    • attaching the appropriate certificate to API Gateway, but
    • do, not point DNS to the assigned regional domain name API Gateway gives you; instead,
    • use the assigned regional endpoint hostname as the Origin Domain Name in CloudFront
    • whitelist the Host header for forwarding

    This should work because it will cause API Gateway to expect the original Host header, coupled with the way CloudFront handles TLS on the back-end when the Host header is whitelisted for forwarding.