How to S3 + Cloudfrond + 2 SPA react apps in subfolders?
Asked Answered
K

1

7

I want to serve 2 different react apps in subfolders using S3 + Cloudfront

myapp.com/app-one/ <-- here is index.html
myapp.com/app-two/ <-- here is another index.html

I know how to configure create-react-app and react router to handle this.
https://create-react-app.dev/docs/deployment/#building-for-relative-paths

The problem is with configuring S3 + Cloudfront to handle redirects.

When you enter url in browser:
myapp.com/app-one/some-route - it should redirect to index.html of app-one
myapp.com/app-two/some-route - it should redirect to index.html of app-two

Unfortunately S3 lets you define only one fallback file.html (404) for Static website hosting

I also don't want to use hashrouter: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md

Kronstadt answered 7/2, 2020 at 22:38 Comment(1)
did you find a solution?Epigoni
E
4

I was struggling with the same problem for an angular application, and found some solutions, but the one that I found most useful is by using a Lambda@Edge function, this allows you to keep your static files inside the S3 bucket without opening to static hosting.

The only Lambda@Edge configuration that worked for me is the one from this answer. Here is the code:

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);
};

What it basically does is to match the uri of your subfolder and redirect all request to the correct index.html file. If you have multiple sub folders you could simply add some conditions:

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;
    var newuri = olduri
    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    if(olduri.match(/first-sub-app.*/)){
        newuri = olduri.replace(/first-sub-app.*/, 'first-sub-app/index.html');
    } else if(olduri.match(/second-sub-app.*/)){
        newuri = olduri.replace(/second-sub-app.*/, 'second-sub-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);
};

Check the original answer for further instructions on the setup and more details on how it works but it basically ignores any request with extensions and only redirects if it matches a specific subdirectory.

Epigoni answered 21/7, 2020 at 5:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.