Redirect All Routes to index.html When Hosting a React/Angular Webapp on S3

If you’re using S3’s static website hosting feature to host a React or Angular webapp, the URLs in the URL bar of a browser running your webapp are essentially pseudo URLs. There’s no HTML page at the end of any of those URLs. Client-side JavaScript in your webapp handles all URLs & displays the appropriate page. In such cases, if a user types-in or visits 1 of the sub-URLs of your webapp, maybe via a bookmark, S3 will simply return a 404.

To avoid this, you must set up redirection of all URLs to index.html, which will load your webapp, which in-turn will handle the URL. In the server world, this would be easily accomplished by a redirect rule:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
rewrite ^(.+)$ /index.html last;

There are many ways to handle this redirection for a static webapp hosted on S3:

Using CloudFront

Create a CloudFront distribution with your S3 bucket as the origin, index.html as the default root object, “<your-bucket>” as the origin domain name, & a custom error response to redirect everything to index.html:

Using S3 Redirection Rules

Using advanced redirection rules, you can route requests conditionally according to specific object key names, prefixes in the request, or response codes.

Configuring Advanced Conditional Redirects — S3 Documentation

In our case, use this redirection rule:


There are a few problems with this approach though:

  • Multiple redirects happen as your app’s paths are resolved.
  • There is a flicker in the url bar as the ‘#’ comes & goes due the action of your SPA framework.
  • SEO is impacted.
  • Safari support is broken!

Using S3 Error Document

Set the S3 error document to index.html:

In this case, even though the user gets the webapp, the status code is still 404, which isn’t good for SEO.

Using Lambda@Edge

Use the compute power at the CloudFront edge to inspect the request as it’s coming in from the client. Then re-write the request so that CloudFront requests a default index object (index.html in this case) for any request URI that ends in ‘/’.

AWS Compute Blog

Lambda Code:

'use strict';
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;

    // 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(/\/$/, '\/index.html');
    // Log the URI as received by CloudFront and the new URI to be used to fetch from origin
    console.log("Old URI: " + olduri);
    console.log("New URI: " + newuri);
    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;
    // Return to CloudFront
    return callback(null, request);


See full article here — AWS Compute Blog.

Using Amplify Redirects

If your webapp is built at the AWS Amplify console, use this:

Most SPA frameworks support HTML5 history.pushState() to change browser location without triggering a server request. This works for users who begin their journey from the root (or /index.html), but fails for users who navigate directly to any other page.

Redirects for Single Page Web Apps (SPA) — Amplify Documentation

Copy the regex from here — Redirects for Single Page Web Apps (SPA).