Nicholas Haley
Nicholas Haley

Reputation: 4024

Client-side routing for S3 / Cloudfront with multiple path-based Single Page Apps

I have the following situation:

/v1.0.0
  index.html
  main.js
/v1.1.0
  index.html
  main.js

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?

Upvotes: 5

Views: 2793

Answers (1)

acorbel
acorbel

Reputation: 1568

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.

Upvotes: 7

Related Questions