问题
I have two static web apps (create-react-apps) that are currently in two separate S3 buckets. Both buckets are configured for public read + static web hosting, and visiting their S3 hosted URLs correctly display the sites.
Bucket 1 - First App:
index.html
static/js/main.js
Bucket 2 - Second App:
/secondapp/
index.html
static/js/main.js
I have setup a single Cloudfront for this - The default cloudfront origin loads FirstApp
correctly, such that www.mywebsite.com loads the index.html by default.
For the SecondApp
, I have set up a Cache Behavior so that the path pattern secondapp/*
points to the SecondApp bucket URL.
In my browser, when I visit www.mywebsite.com/secondapp/
it correctly displays the second web app.
If I omit the trailing slash however, I instead see the First App, which is undesired.
If I visit www.mywebsite.com/secondapp/something
, I am also shown the First App, which is also undesired. (I want it to load the .html of secondapp
)
Both apps are configured to use html5 push state via react-router-dom.
My desired behavior is that visiting the following displays the correct site/bucket:
www.mywebsite.com
- Currently working
www.mywebsite.com/secondapp/
- Currently working
www.mywebsite.com/secondapp
- (Without trailing slash) Not working, shows First App
www.mywebsite.com/secondapp/something_else
- Not working, show First App
How can I achieved the desired behavior?
Thanks!
回答1:
After researching this issue, I was able to resolve it using lambda@edge (https://aws.amazon.com/lambda/edge/)
By deploying a simple javascript function to route specific paths to the desired s3 bucket, we are able to achieve an nginx-like routing setup. The function sits on lambda@edge on our Cloudfront CDN, meaning you can specify when it is triggered. For us, it's on "Origin Request"
My setup is as follows:
- I used a single s3 bucket, and deployed my second-app in a subfolder "second-app"
- I created a new Lambda function, hosted on "U.S. East N Virginia". The region is important here, as you can only host lambda function an @edge in this region.
- See below for the actual Lambda function
- Once created, go to your CloudFront configuration and go to "Behaviors > Select the Default (*) path pattern and hit Edit"
- Scroll to the bottom where there is "Lambda Function Associations"
- Select "Origin Request" form the drop down
- Enter the address for your lambda function (
arn:aws:lambda:us-east-1:12345667890:function:my-function-name
)
Here is an example of the lambda function I used.
var path = require('path');
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda@Edge
var request = event.Records[0].cf.request;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/second-app.*/, 'second-app/index.html');
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
These are the resources I used for this solution:
- https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/
- https://github.com/riboseinc/terraform-aws-s3-cloudfront-website/issues/1
- How do you set a default root object for subdirectories for a statically hosted website on Cloudfront?
来源:https://stackoverflow.com/questions/53692897/hosting-multiple-spa-web-apps-on-s3-cloudfront-under-same-url