46

API Gateway (APIG), while it uses CloudFront (CF) it does not support CDN edge caching. When I configured a CF distribution to use APIG as the custom origin, I get a permission denied error.

How do I configure CF to fix this?

Eduardo
  • 657
  • 1
  • 9
  • 28
rynop
  • 50,086
  • 26
  • 101
  • 112

6 Answers6

81

Until API Gateway (APIG) supports edge caching via its internal use of CloudFront (CF), I have come up with a workaround.

You can indeed put CF dist in front of APIG, the trick is to force HTTPS only "Viewer Protocol Policy" AND to NOT forward the HOST header because APIG needs SNI.

I setup my CF "Default Cache Behavior Settings" to not forward any headers, and forced "Viewer Protocol Policy" to "HTTPS Only" and it works. Hope this helps others.

Here is a CloudFormation resource object that has all the required configuration (Note: I use the convention <stage>--<app name> for StackName):

CloudFront:  
  Type: AWS::CloudFront::Distribution
  Properties:
    DistributionConfig:
      Enabled: true
      IPV6Enabled: true
      HttpVersion: http2
      Comment: !Join [ '--', [!Ref 'AWS::StackName', ' Cloud Front']]
      Aliases: [!Ref CloudFrontCname]
      ViewerCertificate:
        AcmCertificateArn: !Ref AcmCertificateArn
        SslSupportMethod: sni-only
        MinimumProtocolVersion: TLSv1.1_2016
      Origins:
      - Id: APIGOrigin
        DomainName: !Sub
          - ${apigId}.execute-api.${AWS::Region}.amazonaws.com
          - { apigId: !Ref ApiGatewayLambdaProxy }
        OriginPath: !Sub
          - /${Stage}
          - { Stage: !Select [ "0", !Split [ '--', !Ref 'AWS::StackName' ] ] }
        CustomOriginConfig:
          # HTTPPort: 80
          HTTPSPort: 443
          OriginProtocolPolicy: https-only
        OriginCustomHeaders:
          - HeaderName: 'Verify-From-Cf'
            HeaderValue: !Ref VerifyFromCfHeaderVal
      DefaultCacheBehavior:
        AllowedMethods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
        CachedMethods: ["GET", "HEAD", "OPTIONS"]
        ForwardedValues:
          Headers:
          - Access-Control-Request-Headers
          - Access-Control-Request-Method
          - Origin
          - Authorization
          # - Host APIG needs to use SNI
          QueryString: true
        TargetOriginId: APIGOrigin
        ViewerProtocolPolicy: https-only
        Compress: true
        DefaultTTL: 0
      CustomErrorResponses:
      - ErrorCachingMinTTL: 0
        ErrorCode: 400
      - ErrorCachingMinTTL: 1
        ErrorCode: 403
      - ErrorCachingMinTTL: 5
        ErrorCode: 500
DNSARecord:    
  Type: AWS::Route53::RecordSet
  Properties:
    Comment: !Ref 'AWS::StackName'
    Name: !Ref CloudFrontCname
    Type: A
    HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']]
    AliasTarget:
      HostedZoneId: !Ref Route53HostedZoneId
      DNSName: !GetAtt CloudFront.DomainName
DNSAAAARecord:    
  Type: AWS::Route53::RecordSet
  Properties:
    Comment: !Ref 'AWS::StackName'
    Name: !Ref CloudFrontCname
    Type: AAAA
    HostedZoneName: !Join ['.', [ !Select [1, !Split ['.', !Ref CloudFrontCname]], !Select [2, !Split ['.', !Ref CloudFrontCname]], '']]
    AliasTarget:
      HostedZoneId: !Ref Route53HostedZoneId
      DNSName: !GetAtt C

Late 2018 updates

  • CloudFormation finally supports setting SSL proto ver: MinimumProtocolVersion: TLSv1.1_2016
  • I've baked this (and many other) best practices into an OSS project: aws-blueprint
Yves M.
  • 29,855
  • 23
  • 108
  • 144
rynop
  • 50,086
  • 26
  • 101
  • 112
  • 1
    Can you use Path Patterns with this? I have a cloudfront distribution setup with two origins: an S3 bucket for my static files, and an APIG API; but my PathPattern for the APIG doesn't trigger. I think it's because I'm not matching on a file name or type, but rather on a particular path. – Josh Wulf Oct 13 '15 at 14:30
  • 12
    API Gateway uses CloudFront behind the scenes. If you point another CloudFront distribution to your API Gateway endpoint, CloudFront blocks the request because CloudFront has an anti-loopback check. I.e. You can't point CloudFront at CloudFront. I was told by an AWS engineer at AWS re:Invent 2015 that this anti-loopback check will be relaxed to allow you to point a CloudFront distribution to API Gateway. I have not yet verified if it works yet. – Jamey Nov 02 '15 at 20:57
  • I just tested and it's now working, `curl https://api.cloudprowess.com` to see it in action. I do this in order to be able to set a custom domain that uses an SSL certificate issued by the AWS Certificate Manager, which wouldn't be available otherwise to my API Gateway. My question now is if this is going to cost me twice for CloudFront bandwidth and requests, or will the within-CloudFront costs be zero I'll ask this as a new question. – Cristian Măgherușan-Stanciu Oct 13 '16 at 18:33
  • It will cost double. Please reach out to your AWS rep asking for more CloudFront features exposed in APIG - more people that ask the better. – rynop Oct 13 '16 at 19:28
  • You can also whitelist some headers rather than disabling all. As long as the Host header doesn't pass through it seems to work fine. https://www.codeengine.com/articles/process-form-aws-api-gateway-lambda/ – Dave Maple Jan 02 '17 at 21:20
  • 3
    First of all, yes removing the host header is all you need to make this work. I'm curious if anyone has tried this with ApiGateway set to IAM authentication. I'm getting a signature error. It expects a signature for host=apigateway host and not host=cloudfront host – Atif Jan 13 '17 at 20:44
  • 17
    I can't even describe how much time knowing NOT TO Forward the Host header can save people time. – Guy S Jan 29 '17 at 14:46
  • Manually setting a cloud-front for an api gateway endpoint seems to generate random 500 responses (around 20%). These requests do not reach the api endpoint even if TTL is 0. Can others see this as well? – Luis Cazacu Feb 01 '17 at 13:58
  • Two important points that I ran into - I had to set my Api Gateway OriginProtocolPolicy to `https-only`. Additionally, because I was using CORS with openid connect, I also needed to whitelist my `Origin` and `Authorization` headers under Behaviors. – killthrush Nov 18 '17 at 23:58
  • Can you set CF not to forward the headers from the API Console? I can't find this setting anywhere. I also tried overriding the Host field from in the origin definition, but the console did not allow it. – Hamish Moffatt Apr 13 '18 at 04:38
  • How did you determine the value of `DistributionConfig.Origins.Id` and `DistributionConfig.DefaultCacheBehavior.TargetOriginId`? Is this something you customized or was it set for you somewhere by AWS? – Kwhitejr Jul 16 '18 at 19:17
  • Thanks! However in the `ViewerProtocolPolicy` shouldn't we put `redirect-to-https`? – Matteo Jan 09 '20 at 03:42
13

Adding to previous answers:

it's important that behavior path pattern is actually something that matches "real" path.


If API endpoint is <id>.execute-api.<region>.amazonaws.com/stage-name/my-api

And origin domain + path is <id>.execute-api.<region>.amazonaws.com/stage-name

Behavior path pattern has to be my-api, my-api/*, my-api/something, etc


I don't know why, but I thought that path pattern can be used as alias, for example:

https://www.example.com/random-name (path pattern random-name) resolves to domain + path set in origin, e.g <id>.execute-api.<region>.amazonaws.com/stage-name.

That's not the case.

Solo
  • 6,687
  • 7
  • 35
  • 67
  • 1
    thank you! in my case I implemented this with an api gateway resource that matches the path pattern. api: gateway: `.execute-api..amazonaws.com/stage-name/api/endpoint1`, origin +path: `.execute-api..amazonaws.com/stage-name`, path patten: `/api/*`, final endpoint: `https://.cloudfront.net/api/endpoint1`, not related but I also found this answer useful for passing headers: https://stackoverflow.com/a/47380572/728602 – Cadell Christo Aug 13 '20 at 03:58
  • 2
    Here's an implementation in AWS CDK: https://github.com/cadbox1/backend-frontend-aws-cdk/blob/master/lib/lambda-cdk-stack.ts – Cadell Christo Aug 13 '20 at 13:15
  • This was really helpful, thanks! I had to remove originPath from my cloudfront behavior config and make sure my pathPattern matched my stage name in API gateway. – Dakota Hipp May 30 '23 at 21:10
4

If API Gateway returns a 403 error with:

Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header.

it can also be that the origin endpoint is incorrect. "API Gateway treats all errors to non-existent paths as 403 permission denied errors rather than a 404 not found error." (see this support thread).

I got this error and assumed I was incorrectly forwarding the Authorization header, but I had simply misconfigured the origin path.

Kevin
  • 682
  • 1
  • 9
  • 18
  • It's very common to treat 'resource not found' as 'access denied' if the caller does not have list permissions. Otherwise you are leaking information about the names of resources, and that potentially has value to an attacker. – jarmod Feb 13 '19 at 21:36
3

I just want to reiterate here, for anyone who's following the guide from AWS premium support knowledge center,

How do I set up API Gateway with my own CloudFront distribution? https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudfront-distribution/

If you are using AWS console to set up the CloudFront distribution, the root cause is that you set Cache Based on Selected Request Headers to all.

enter image description here

Setting it to None or exclude the Host header in the Whitelist would solve the problem.

jellycsc
  • 10,904
  • 2
  • 15
  • 32
0

With the launch of API Gateway regional endpoints in Nov 2017 I believe it is now optimal to use these with CloudFront Distributions on top. Some detailed instructions for moving from Edge Optimized API's to Regional API's and setting up the CloudFront Distributions are here:

AWS API Gateway should prevent use of TLS v1

Alistair
  • 306
  • 3
  • 7
0

You could use the behavior and origin feature of cloudfront.

Have multiple origins for example S3 bucket and another api gateway.

Then based on behavior you can route to specific origin.

Like Default(*) behavior will point to S3.

/api/* behavior will point to api gateway.

https://kuchbhilearning.blogspot.com/2022/10/add-cloudfront-behavior-and-origin.html code.

Much detail explanation https://kuchbhilearning.blogspot.com/2022/10/api-gateway-and-cloud-front-in-same.html

dead_webdev
  • 164
  • 2
  • 9