I’m using an S3 website endpoint as the origin of my CloudFront distribution. Why am I getting 403 Access Denied errors?

Last updated: 2022-10-12

I'm using an Amazon Simple Storage Service (Amazon S3) bucket as the origin of my Amazon CloudFront distribution. How can I troubleshoot 403 access denied errors?

Short description

To troubleshoot Access Denied errors, first determine if your distribution's origin domain name is an S3 website endpoint or an S3 REST API endpoint. Then, if your distribution is using a website endpoint, review the troubleshooting sections.

Resolution

Determine your distribution origin domain name's endpoint type

1.    Open the CloudFront console.

2.    Choose your CloudFront distribution. Then, choose Distribution Settings.

3.    Choose the Origins and Origin Groups tab.

4.    Review the domain name under Origin Domain Name and Path. Determine the endpoint type based on the format of the domain name:

Rest API endpoints use the following format:

DOC-EXAMPLE-BUCKET.s3.amazonaws.com

Website endpoints use the following format:

DOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com

Note: Depending on the AWS Region, the endpoint format can be in the dash format (s3-website-Region) or the dot format (s3-website.Region). If your distribution is using a REST API endpoint, see I'm using an S3 REST API endpoint as the origin of my CloudFront distribution. Why am I getting 403 Access Denied errors?

If your distribution is using a website endpoint, verify the requirements in the following sections to avoid Access Denied errors.

Confirm that the objects in the bucket aren't encrypted by AWS Key Management Service (AWS KMS)

CloudFront distributions don't support AWS KMS-encrypted objects. You must remove AWS KMS encryption from the S3 objects that you want to serve using the distribution. Instead of using AWS KMS encryption, use AES-256 to encrypt your objects.

Determine if the objects are AWS KMS-encrypted

To check if the objects in your bucket are KMS-encrypted:

Use the Amazon S3 console to view the properties of the object. Review the Encryption dialog box. If AWS-KMS is selected, then the object is AWS KMS-encrypted.

-or-

Run the head-object command using the AWS Command Line Interface (AWS CLI). If the command returns ServerSideEncryption as aws:kms, then the object is AWS KMS-encrypted.

Note: If you receive errors when running AWS CLI commands, make sure that you're using the most recent version of the AWS CLI .

Change an object's encryption settings

To change the object's encryption settings using the Amazon S3 console, see Specifying server-side encryption with AWS KMS (SSE-KMS).

To change the object's encryption settings using the AWS CLI, first verify that the object's bucket doesn't have default encryption. If the bucket doesn't have default encryption, then run the following command to remove the object's encryption by copying the object over itself. Replace DOC-EXAMPLE-BUCKET with the name of your bucket:

aws s3 cp s3://DOC-EXAMPLE-BUCKET/index.html s3://DOC-EXAMPLE-BUCKET/index.html

Warning: Copying the object over itself removes settings for storage-class and website-redirect-location. To maintain these settings in the new object, be sure to explicitly specify these values in the copy request.

Confirm that there is no explicit "Deny" in the bucket policy for the s3:GetObject action

Your bucket policy must not have a deny statement that blocks public read access to the s3:GetObject action.

If you have an explicit allow statement for s3:GetObject in your bucket policy, confirm that there isn't a conflicting explicit deny statement. An explicit deny statement always overrides an explicit allow statement.

To review your bucket policy for s3:GetObject:

1.    Open your S3 bucket from the Amazon S3 console.

2.    Choose the Permissions tab.

3.    Choose Bucket Policy.

4.    Review the bucket policy for statements with "Action": " s3:GetObject" or "Action": " s3:*".

5.    Modify the bucket policy to remove or edit statements that block public read access to s3:GetObject.

For example, the following policy contains an explicit allow statement for public access to s3:GetObject. However, it also contains an explicit deny statement for s3:GetObject access, unless the request is from a specific Amazon Virtual Private Cloud (Amazon VPC). This policy would have to be modified to allow the s3:GetObject action.

{
  "Version": "2008-10-17",
  "Id": "PolicyForCloudFrontPrivateContent",
  "Statement": [
    {
      "Sid": "Allow-OAI-Access-To-Bucket",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EAF5XXXXXXXXX"
      },
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
      ]
    },
    {
      "Sid": "Allow-Public-Access-To-Bucket",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
      ]
    },
    {
      "Sid": "Access-to-specific-VPCE-only",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:sourceVpce": "vpce-1a2b3c4d"
        }
      }
    }
  ]
}

If the bucket policy grants public read access, confirm that AWS account that owns the bucket also owns the object

For a bucket policy to allow public read access to objects, the AWS account that owns the bucket must also own the objects. For existing Amazon S3 buckets with the default object ownership settings, the object owner is the AWS account of the AWS Identity and Access Management (IAM) identity which uploaded the object to the bucket.

Note: The object-ownership requirement applies to public read access granted by a bucket policy. It doesn't apply to public read access granted by the object's access control list (ACL).

Confirm that the bucket and objects have the same owner

Use the following steps to check if the bucket and objects have the same owner. You can also use the Amazon S3 console to check the bucket and object owners. The owners are found in the Permissions tab of the respective bucket or object.

1.    Run the following AWS CLI command to get the S3 canonical ID of the bucket owner:

aws s3api list-buckets --query Owner.ID

2.    Run the following command to get the S3 canonical ID of the object owner:

aws s3api list-objects --bucket DOC-EXAMPLE-BUCKET --prefix index.html

This example shows a single object, but you can use the list command to check several objects.

3.    If the canonical IDs don't match, then the bucket and object have different owners.

Update object ownership

Bucket owners can manage ownership of objects with S3 Object Ownership. All new S3 buckets have the bucket owner enforced setting turned on by default. To update an existing bucket, see Setting Object Ownership on an existing bucket. When the bucket owner enforced setting is turned on, bucket owners are the object owners for all objects inside the bucket. Also, when the bucket owner enforced setting is turned on, any ACLs on a bucket and its objects are deactivated.

It's a best practice that bucket owners use the bucket owner enforced setting on all buckets, and manage permissions through IAM and bucket policies.

To remove ACLs for your bucket and to take ownership of all objects in the bucket, run the following command:

aws s3api put-bucket-ownership-controls --bucket example-bucket --ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'

If you don't want to turn off the ACLs on your S3 bucket, you can also change the object's owner to the bucket owner by following these steps:

1.    From the object owner's account, run this command to retrieve the ACL permissions assigned to the object:

aws s3api get-object-acl --bucket DOC-EXAMPLE-BUCKET --key object-name

2.    If the object has bucket-owner-full-control ACL permissions, then skip to step 3. If the object doesn't have bucket-owner-full-control ACL permissions, then run the following command from the object owner's account. Replace DOC-EXAMPLE-BUCKET with the name of your bucket.

aws s3api put-object-acl --bucket DOC-EXAMPLE-BUCKET --key object-name --acl bucket-owner-full-control

3.    From the bucket owner's account, run the following command to change the owner of the object by copying the object over itself. Replace DOC-EXAMPLE-BUCKET with the name of your bucket.

aws s3 cp s3://DOC-EXAMPLE-BUCKET/index.html s3://DOC-EXAMPLE-BUCKET/index.html

Confirm that the requested objects exist in the bucket

If the user doesn't have S3:ListBucket permissions, then the user gets Accessed Denied errors for missing objects instead of 404 Not Found errors. Run the head-object AWS CLI command to check if an object exists in the bucket.

Note: Confirm that the object request sent to CloudFront matches the S3 object name exactly. S3 object names are case sensitive. If the request doesn't have the correct object name, then Amazon S3 responds as though the object is missing. To identify what object that CloudFront is requesting from Amazon S3, use server access logging.

If you receive an error regarding your default root object, then make sure your object name doesn't have any extra characters. For example, index.html should be entered in the Default Root Object field, and not /index.html. For more information, see How to specify a default root object.

If the object exists in the bucket, then the Access Denied error isn't masking a 404 Not Found error. Verify other configuration requirements to resolve the Access Denied error.

If the object isn't in the bucket, then the Access Denied error is masking a 404 Not Found error. Resolve the issue related to the missing object.

Note: It's not a security best practice to activate public s3:ListBucket access. Turning on public s3:ListBucket access allows users to see and list all objects in a bucket. This exposes object metadata details (for example, key and size) to users even if the users don't have permissions for downloading the object.

Confirm that Amazon S3 Block Public Access is turned off for the bucket

Confirm that there aren't any Amazon S3 Block Public Access settings applied to the bucket. These settings can override permissions that allow public read access. Amazon S3 Block Public Access settings can apply to individual buckets or AWS accounts.

Confirm that objects in the bucket are publicly accessible

A distribution using a website endpoint supports only publicly accessible content. To determine if an object in your S3 bucket is publicly accessible, open the object's URL in a web browser. Or, run a curl command on the URL.

For example:

http://DOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com/index.html

If the web browser or curl command returns an Access Denied error, then the object isn't publicly accessible.

To allow public read access:

Create a bucket policy that allows public read access for all objects in the bucket.

-or-

Use the Amazon S3 console to allow public read access for the object.

If Requester Pays is turned on, confirm that the request includes the request-payer parameter

If Requester Pays is turned on for a bucket, then anonymous access to the bucket isn't allowed. Users from other accounts must specify the request-payer parameter when they send requests to the bucket. Otherwise, those users get an Access Denied error.

If you're using a Referer header to restrict access from CloudFront to your S3 origin, then review the custom header

If you're using the Referer header to restrict access from CloudFront to your S3 website endpoint origin, check the secret value or token set on the S3 bucket policy. Then, confirm that the secret value or token matches the value on the CloudFront origin custom header.

If you're using an explicit deny statement in the bucket policy, then confirm that there's also an allow statement that grants access based on the Referer header. You can't grant access with only an explicit deny statement.

For example, the following bucket policy grants access to the S3 origin when the request contains the string "aws:Referer":"MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER."
The CloudFront origin custom header must be:

  • Header: Referer
  • Value: MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER
{
  "Version":"2012-10-17",
  "Id":"http referer policy example",
  "Statement":[
    {
      "Sid":"Allow get requests originating from my CloudFront with referer header",
      "Effect":"Allow",
      "Principal":"*",
      "Action":"s3:GetObject",
      "Resource":"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
      "Condition":{
        "StringLike":{"aws:Referer":"MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER"}
      }
    }
  ]
}

Note: The example bucket policy grants public (anonymous) access to the bucket because the Principal is a wildcard value ("Principal":"*"). However, because of the condition statement, access to the S3 origin is granted only if the request includes the Referer header and the header value matches the value in the bucket policy.

Confirm that there's no explicit "Deny" Service control policies (SCPs) attached by your organization's management account

Service control policies (SCPs) are a type of organization policy that you can use to manage permissions in your organization. Using your organization's management account in AWS Organizations to check if there is a deny policy (for s3:GetObject action) attached to the organization root, to the organizational unit (OU), or directly to your AWS account.