一覧に戻る
Security & CDN

S3 & CloudFront 静的ウェブサイトホスティング

Amazon S3でコンテンツをホストし、Amazon CloudFront、Route 53、ACMを組み合わせてHTTPS対応の安全で高速な静的サイトを作成します。

構成要素 (AWS Services):

S3CloudFrontRoute 53ACM

アーキテクチャ図 (Architecture Diagram)

クリックで拡大表示
S3 & CloudFront 静的ウェブサイトホスティング アーキテクチャ図

AWS CLI でのデプロイ例

AWS CLIを使用してCloudFormationスタックをデプロイする場合は、以下のコマンドを実行します。

aws cloudformation create-stack \
  --stack-name s3-static-website-stack \
  --template-body file://s3-static-website.yaml \
  --capabilities CAPABILITY_IAM
s3-static-website.yaml
DL
AWSTemplateFormatVersion: '2010-09-09'
Description: 'S3 static website hosting with CloudFront - Best Practices'

Resources:
  LogBucket:
    Type: AWS::S3::Bucket
    Metadata:
      Comment: Access logs bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldLogs
            Status: Enabled
            ExpirationInDays: 90

  LogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref LogBucket
      PolicyDocument:
        Statement:
          - Sid: S3ServerAccessLogsPolicy
            Effect: Allow
            Principal:
              Service: logging.s3.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub ${LogBucket.Arn}/*
          - Sid: DenyInsecureTransport
            Effect: Deny
            Principal: '*'
            Action: s3:*
            Resource:
              - !GetAtt LogBucket.Arn
              - !Sub ${LogBucket.Arn}/*
            Condition:
              Bool:
                aws:SecureTransport: false

  ContentBucket:
    Type: AWS::S3::Bucket
    DependsOn: LogBucketPolicy
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName: !Ref LogBucket
        LogFilePrefix: content-bucket-logs/
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpirationInDays: 90

  ContentBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref ContentBucket
      PolicyDocument:
        Statement:
          - Sid: AllowCloudFrontServicePrincipal
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource: !Sub ${ContentBucket.Arn}/*
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
          - Sid: DenyInsecureTransport
            Effect: Deny
            Principal: '*'
            Action: s3:*
            Resource:
              - !GetAtt ContentBucket.Arn
              - !Sub ${ContentBucket.Arn}/*
            Condition:
              Bool:
                aws:SecureTransport: false

  CloudFrontOriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: !Sub ${AWS::StackName}-OAC
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultRootObject: index.html
        HttpVersion: http2and3
        IPV6Enabled: true
        Origins:
          - Id: S3Origin
            DomainName: !GetAtt ContentBucket.RegionalDomainName
            S3OriginConfig:
              OriginAccessIdentity: ''
            OriginAccessControlId: !Ref CloudFrontOriginAccessControl
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          CachedMethods:
            - GET
            - HEAD
          Compress: true
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6  # CachingOptimized
        CustomErrorResponses:
          - ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        ViewerCertificate:
          CloudFrontDefaultCertificate: true
          MinimumProtocolVersion: TLSv1.2_2021

Outputs:
  BucketName:
    Value: !Ref ContentBucket
    Description: S3 bucket name for static content
  DistributionId:
    Value: !Ref CloudFrontDistribution
    Description: CloudFront distribution ID
  DistributionDomainName:
    Value: !GetAtt CloudFrontDistribution.DomainName
    Description: CloudFront distribution domain name
  WebsiteURL:
    Value: !Sub https://${CloudFrontDistribution.DomainName}
    Description: Website URL