skip to Main Content

My project has a simple static website as the root (literally a single index.html file) and and Angular app that should be displayed with the user clicks on a navigation link. I have configured CloudFront + S3 using OAC as described in this AWS blog post.

It works perfectly fine for the root website. For example when the user navigates to mydomain.com the basic static website displays as expected. The problem I am having is when the user attempts to navigate to mydomain.com/myApp I get a 403 Access Denied.

On the S3 side I have individual buckets for the root app and the Angular app.
The file structure for the Angular app in S3 is MyBucket/myApp/Angular build files.
The bucket policy in place is the default policy that AWS provides when you assign the OAC from CloudFront, shown below:

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::<my-bucket>/*",
                "arn:aws:s3::: <my-bucket>/myApp/*" <--Added as attempt to solve 403
            ],
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::<numbers>:distribution/<id>"
                }
            }
        }
    ]
}

On the CloudFront side I have a single distribution with the default origin pointing to the S3 bucket with the basic static website (index.html file) and an additional origin pointing to the S3 bucket with the Angular app.

In addition, I have configured a Behaviors pattern with a Path Pattern = myApp/* with the origin= S3 bucket with Angular app and "viewer Protocol Policy=HTTP & HTTPS. All other configuration in the Behavior pattern were left at default.

On the Angular side I have configured the app to use a base href="/myApp/"

I’ve tried so many different AWS configurations like setting an origin path = /myApp in CloudFront, setting Viewer Protocol Policy= redirect HTTP to HTTPS. I’ve tried flattening the directory structure in S3 to remove the myApp dir and have all the Angular build files at the root. I’ve been hitting dead ends all over and haven’t been able to find a solution on StackOverflow or broader internet. I may just be asking the wrong questions, but I’m really stuck here. Any solutions or insight are greatly appreciated!

2

Answers


  1. "arn:aws:s3:::<my-bucket>/*" is enough. "arn:aws:s3:::<my-bucket>/myApp/*" is a subset of the first.

    You have a different issue here. To solve the issue you can use 1. or 2. from the below:

    1. Use Edge lambda for VIEWER_REQUEST events to redirect requests to index.html
    "use strict";
    const map = {
      "/myApp": "/index.html",
      "/other/path": "/index.html",
    };
    
    
    // eslint-disable-next-line
    exports.handler = (event, context, callback) => {
      // Extract the request from the CloudFront event that is sent to Lambda@Edge
      const request = event.Records[0].cf.request;
    
      // Extract the URI from the request
      const oldUri = request.uri;
      const newUri =
        map[
          oldUri.endsWith("/") ? oldUri.substring(0, oldUri.length - 1) : oldUri
        ] || oldUri;
      // Log the URI as received by CloudFront and the new URI to be used to fetch from origin
      // console.log("map", { oldUri, newUri });
      // Replace the received URI with the URI that includes the index page
      request.uri = newUri;
      // Return to CloudFront
      return callback(null, request);
    };
    
    
    1. Pre-generate and upload your site in a different way.
      CouldFront literally requires path/to/something object to be present at S3 for urls like https://host/path/to/something.
      So you can use something similar to ‘react-snap’ for Angular to generate static HTML for all required paths and upload the correct structure to s3.

    Mind that for object s3://bucket/path/index.html the https://host/path will not work with CloudFront. CloudFront needs to have s3://bucket/path where path has content of index.html

    Login or Signup to reply.
  2. You mention that you have two different buckets: one for the root app and another for the Angular app, but it seems that the resource policy shown in the code snippet is attached only to the root app bucket. You need to attach a similar policy to the Angular app bucket.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search