Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you add CloudFront in front of API Gateway

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?

like image 588
rynop Avatar asked Sep 28 '15 14:09

rynop


People also ask

Should I put CloudFront in front of Alb?

Neither option is wrong, but using CloudFront in front of ALB does provide some advantanges even for non-cacheable, dynamic content -- including faster TLS negotiation for viewers who are more distant from the ALB and optimized routing of requests, globally on the AWS Edge Network, from an edge location near the viewer ...

Can CloudFront cache API gateway?

CloudFront has a built-in caching capability. It's the first place you should consider caching on the server-side. Caching at the edge is very cost-efficient as it cuts out most of the calls to API Gateway and Lambda. Skipping these calls also improve the end-to-end latency and ultimately the user experience.


1 Answers

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 CloudFront.DomainName 

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
like image 100
rynop Avatar answered Sep 22 '22 19:09

rynop