Deliver S3 content via CloudFront using OAC

Deliver S3 content via CloudFront using OAC AWS_EN

Restricting access to origin with OAC when delivering S3 content from CloudFront

The following page shows how to use OAI to restrict access to origin when delivering S3 bucket content from CloudFront.

OAC (Origin Access Control) was announced in August 2022.
AWS officially recommends using OAC rather than OAI.

We recommend using OAC because it supports:

All Amazon S3 buckets in all AWS Regions, including opt-in Regions launched after December 2022

Amazon S3 server-side encryption with AWS KMS (SSE-KMS)

Dynamic requests (PUT and DELETE) to Amazon S3

Restricting access to an Amazon S3 origin

In this article, we will see how to access S3 buckets via CloudFront using OAC.


Diagram of delivering S3 content via CloudFront using OAC

The overall configuration is the same as in the opening page.
Create an OAC for CloudFront and configure the S3 bucket policy to allow access from this OAC.

CloudFormation template files

Build the above configuration with CloudFormation.
The CloudFormation templates are located at the following URL

awstut-fa/096 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

Explanation of key points of the template files

S3 bucket

Resources: Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref Prefix AccessControl: Private
Code language: YAML (yaml)

This is the bucket where the contents are placed.
No special configuration is required on the S3 bucket side when using OAC.


Resources: OAC: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Name: !Ref Prefix OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4
Code language: YAML (yaml)

There are three important parameters for creating an OAC.

The first is the OriginAccessControlOriginType property.
This parameter can only be taken by “s3”.

The second is the SigningBehavior property.
This parameter is related to whether CloudFront signs the origin (S3) when accessing it.
AWS officially mentions the following

We recommend most customers use “Sign requests” option as it ensures your applications will always work because CloudFront will always sign the incoming request. Additionally, by having CloudFront to sign your requests, your applications’ performance is improved as less data is transferred between client and CloudFront.

Amazon CloudFront introduces Origin Access Control (OAC)

Follow the above and set it to “always” so that a signature request is always made.

The third is the SigningProtocol property.
This parameter can only take “sigv4”.


Resources: Distribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: DefaultCacheBehavior: AllowedMethods: - GET - HEAD CachedMethods: - GET - HEAD Compress: true ForwardedValues: Cookies: Forward: none QueryString: false TargetOriginId: !Ref BucketName ViewerProtocolPolicy: allow-all DefaultRootObject: index.html Enabled: true Origins: - DomainName: !Ref BucketRegionalDomainName Id: !Ref BucketName #CustomOriginConfig: OriginAccessControlId: !GetAtt OAC.Id S3OriginConfig: OriginAccessIdentity: "" PriceClass: PriceClass_All
Code language: YAML (yaml)

The key item in the CloudFront configuration for using OAC is the Origins property.
The ID of the OAC defined earlier is set to the OriginAccessControlId property.

The S3OriginConfig property is also important.
Specify an empty string for the internal OriginAccessIdentity property.
If you do not set the above, the following error will occur when creating the CloudFormation stack.

Invalid request provided: Exactly one of CustomOriginConfig and S3OriginConfig must be specified

Note that the CustomOriginConfig property is a parameter for S3 buckets with static website hosting enabled, so do not set it.

Bucket Policy

Resources: BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref BucketName PolicyDocument: Statement: Action: - s3:GetObject Effect: Allow Principal: Service: - Resource: !Sub "arn:aws:s3:::${BucketName}/*" Condition: StringEquals: AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${Distribution}"
Code language: YAML (yaml)

Define the bucket policy with reference to the following official AWS page.

Restricting access to an Amazon S3 origin - Amazon CloudFront
Restrict access to an Amazon S3 origin with Amazon CloudFront origin access control (OAC).

If the aforementioned CloudFront distribution is the principal, the content allows the action to retrieve the object.

(Reference) CloudFormation Custom Resources

Resources: CustomResource: Type: Custom::CustomResource Properties: ServiceToken: !GetAtt Function.Arn Function: Type: AWS::Lambda::Function Properties: Code: ZipFile: | import boto3 import cfnresponse import os bucket_name = os.environ['BUCKET_NAME'] object_name = 'index.html' object_body = """<html> <head></head> <body> <h1>index.html</h1> <p>{bucket_name}</p> </body> </html>""".format(bucket_name=bucket_name) content_type = 'text/html' char_code= 'utf-8' s3_client = boto3.client('s3') CREATE = 'Create' DELETE = 'Delete' response_data = {} def lambda_handler(event, context): try: if event['RequestType'] == CREATE: put_response = s3_client.put_object( Bucket=bucket_name, Key=object_name, Body=object_body.encode(char_code), ContentEncoding=char_code, ContentType=content_type) print(put_response) elif event['RequestType'] == DELETE: list_response = s3_client.list_objects_v2( Bucket=bucket_name) for obj in list_response['Contents']: delete_response = s3_client.delete_object( Bucket=bucket_name, Key=obj['Key']) print(delete_response) cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data) except Exception as e: print(e) cfnresponse.send(event, context, cfnresponse.FAILED, response_data) Environment: Variables: BUCKET_NAME: !Ref BucketName FunctionName: !Sub "${Prefix}-function" Handler: !Ref Handler Runtime: !Ref Runtime Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)

Use a CloudFormation custom resource to automatically create an object in an S3 bucket.
In this case, we will create index.html in the aforementioned S3 bucket.

For more information, please refer to the following page


Using CloudFormation, build this environment and check the actual behavior.

Create CloudFormation stacks and check resources in stacks

Create a CloudFormation stack.
For information on how to create stacks and check each stack, please refer to the following page

After checking the resources in each stack, information on the main resources created this time is as follows

  • CloudFront distribution: E3YPSL0CON945
  • CloudFront domain name:
  • OAC: fa-096
  • S3 bucket: fa-096

Confirm the created resource from AWS Management Console.
Check the OAC.

Detail of CloudFront 1.

OAC is created.
You can see that it is configured to always enforce signing with SigV4.

You can also see that it is associated with the CloudFront distribution.

Detail of CloudFront 2.

If you check the configuration page on the CloudFront distribution side, you will see that the OAC setting is enabled.

We can also see that an S3 bucket is specified as the origin.

Detail of S3 1 .

The index.html file is placed in the bucket.
The HTML file was automatically generated and placed by the Lambda function associated with the CloudFormation custom resource.

Detail of S3 2.

Checking the bucket policy, we can see that it allows access to the S3 bucket with CloudFront as the principal.

Check Operation

Now that everything is ready, access the CloudFront distribution.

Detail of CloudFront 3.

We were able to access the HTML file (index.html) in the S3 bucket.
This means that access to objects in the bucket, originally restricted by the bucket policy, is now allowed via CloudFront by using OAC.

Next, access the S3 bucket directly.

Detail of CloudFront 4.

This means that the bucket policy is in action and direct access is restricted.


We have shown how to access S3 buckets via CloudFront using OAC.