5

I have the following situation:

  • An S3 bucket with multiple path-based applications, grouped by version number. Simplified example:
/v1.0.0
  index.html
  main.js
/v1.1.0
  index.html
  main.js
  • Each application is a (React) SPA and requires client-side routing (via React router)

I am using S3 with Cloudfront and have everything mostly working, however the client-side routing is broken. This is to say I am able to visit the root of each application, ie. https://<app>.cloudfront.net/<version>, but cannot reach any client-side routes.

I'm aware that an error document can be set to redirect to an index.html, but I believe this solution only works when there is one index.html per bucket (ie. I cannot set an error document per route-based path).

What's the best way to get around this issue?

Nicholas Haley
  • 3,558
  • 3
  • 29
  • 50

1 Answers1

6

One simple way to deal with SPA through Cloudfront is by using Lambda@Edge - Origin request (or Cloudfront functions). The objective is to change the Origin URI.

A simple js code that I use very often for SPAs (for the v1.0.0 webapp):

exports.handler = async (event) => {
   const request = event.Records[0].cf.request;
   const hasType = request.uri.split(/\#|\?/)[0].split('.').length >= 2;
   if (hasType) return request; // simply forward to the S3 object as it is an asset
   request.uri = '/v1.0.0/index.html'; // handle all react routes
   return request;
};

I check if there is an extension (.png, .js, .css, ...) in the URL. If it is an asset, I simply forward to the S3 object otherwise I send the index.html.
In that case, index.html is sent for the path /v1.0.0/my-react-router.

Updated
For dynamic handling, you can do like this (for the idea):
request.uri = '/' + request.uri.split('/')[1] + '/index.html';

Or even better, use regexp to parse the request.uri in order to extract the version, the asset's extension or the spa route.

acorbel
  • 1,300
  • 12
  • 14
  • Thanks! This is helpful. The only downside is it appears I would need to hard-code the version? I'll be uploading frequent versions, ideally the handler would be able to route to the correct version dynamically – Nicholas Haley May 19 '21 at 15:02
  • Do you have everytime the version in the path? I can change the answer for a dynamic route handling. – acorbel May 19 '21 at 16:01
  • 1
    @NicholasHaley, I've added an example to handle the version. You can optimize the code by parsing the path one time with regexp. – acorbel May 19 '21 at 16:37
  • Happy to confirm that (mostly) everything worked! The only thing is the asset check `hasType` doesn't work with my particular URL since the version number contains periods and creates a false positive – Nicholas Haley May 19 '21 at 19:09