CloudFrontのオリジンにAPI Gatewayを設定して、CloudFront経由でAPI Gatewayにアクセスしてみる

CloudFront経由でAPI Gatewayにアクセスしてみました。
2024.02.15

「Webアプリ」と「Webアプリが使うAPI」を作成しているとき、同一オリジンにしたいことがあります。

  • Webアプリ: example.com
  • Webアプリが使うAPI: example.com/v1/hello

というわけで、試してみました。

おすすめの方

  • CloudFront DistributionをCloudFormationで作成したい方
  • CloudFront DistributionのオリジンにAPI Gatewayを設定したい方

APIを作成する

sam init

sam init \
    --runtime python3.11 \
    --name api-gateway-cloudfront-sample \
    --app-template hello-world \
    --no-tracing \
    --no-application-insights \
    --structured-logging \
    --package-type Zip

AWS SAMテンプレート

CloudFrontに対して、「S3バケット」と「API Gateway」のオリジンを設定しています。 CloudFrontの各種設定は、必要に応じて変更してください。

今回はシンプルな方法です。Pathをさらにカスタマイズしたい場合は、CloudFront Funstionsを利用してください。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: api-gateway-cloudfront-sample

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: v1
      OpenApiVersion: 3.0.1

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.11
      Timeout: 5
      Architectures:
      - x86_64
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
            RestApiId: !Ref MyApi

  HelloWorldFunctionLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}



  SampleBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub api-gateway-sample-${AWS::AccountId}-${AWS::Region}
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  SampleBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref SampleBucket
      PolicyDocument:
        Id: SampleBucket-BucketPolicy
        Statement:
          - Effect: Allow
            Action:
              - s3:GetObject
            Resource:
              - !Sub arn:aws:s3:::${SampleBucket}/*
            Principal:
              CanonicalUser: !GetAtt SampleBucketCloudFrontOriginAccessIdentity.S3CanonicalUserId

  SampleBucketCloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties: 
      CloudFrontOriginAccessIdentityConfig: 
        Comment: !Sub Allows CloudFront to reach the ${SampleBucket}


  SampleDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultRootObject: index.html
        CustomErrorResponses:
          - ErrorCachingMinTTL: 300
            ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCachingMinTTL: 300
            ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        Origins:
          - Id: !Sub S3-${SampleBucket}
            DomainName: !GetAtt SampleBucket.RegionalDomainName
            S3OriginConfig:
              OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${SampleBucketCloudFrontOriginAccessIdentity}
          - Id: !Sub API-Gateway-${MyApi}
            DomainName: !Sub ${MyApi}.execute-api.${AWS::Region}.amazonaws.com
            CustomOriginConfig:
              OriginProtocolPolicy: https-only
        DefaultCacheBehavior:
          TargetOriginId: !Sub S3-${SampleBucket}
          AllowedMethods:
            - GET
            - HEAD
          ViewerProtocolPolicy: redirect-to-https
          # CachingDisabled
          # https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
        CacheBehaviors:
          - PathPattern: 'v1/*'
            TargetOriginId: !Sub API-Gateway-${MyApi}
            AllowedMethods:
              - GET
              - HEAD
              - OPTIONS
              - PUT
              - PATCH
              - POST
              - DELETE
            CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
            ViewerProtocolPolicy: redirect-to-https
        HttpVersion: http2

Outputs:
  HelloWorldApi:
    Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/v1/hello/"

Lambdaコード

「User-Agent」を返します。

app.py

import json


def lambda_handler(event, context):
    return {
        "statusCode": 200,
        "body": json.dumps(
            {
                "message": "hello world",
                "userAgent": event["headers"]["User-Agent"],
            }
        ),
    }

デプロイ

sam build
sam deploy \
    --guided \
    --region ap-northeast-1 \
    --stack-name api-gateway-cloudfront-sample-stack

動作を確認する

事前準備:S3バケットに適当なindex.htmlを格納する

S3バケットに対するアクセスも確認したいので、適当なindex.htmlを作成して格納しておきます。

index.html

this is test file.

まずは、API Gatewayに直接アクセスする

アクセスできました。userAgentはcurlになっています。

$ curl https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello

{"message": "hello world", "userAgent": "curl/8.1.2"}

CloudFrontにアクセスする

さきほど格納したindex.htmlが返ってきました。

$ curl https://zzz.cloudfront.net

this is test file.

CloudFront経由でAPI Gatewayにアクセスする

アクセスできました。userAgentが「Amazon CloudFront」になっています。

$ curl https://zzz.cloudfront.net/v1/hello

{"message": "hello world", "userAgent": "Amazon CloudFront"}

参考