react router doesn’t work in aws s3 bucket

I deployed my React website build/ folder into an AWS S3 bucket.

If I go to, it works and if I click on a tag to go to Project and About pages, it leads me to the right page.
However, if I copy and send the page url or go straight to the link like:, it returns 404.

Here’s my App.js code:

const App = () => (
                <Route exact path="/" component={Home}/>
                <Route exact path="/projects" component={Projects}/>
                <Route exact path="/about" component={About}/>
                <Route component={NoMatch}/>


Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

I’m not sure if you have already solved it. I had the same issue.

It’s because AWS S3 is looking for that folder (projects) to serve from, which you don’t have.

Simply point Error document to index.html.

enter image description here

This worked for me. You can handle error pages in react-router.

Method 2

Update 1 May 2020:

Since this post is quite active, I need to update the answer:

So you have a few options to solve the issue:

  1. You can put index.html in the Error document box (like Alan Friedman suggested).
    • Go to your bucket (the one that actually has the code – not the one you use to redirect) -> Properties -> Static website hosting:
    • This is not “hacky” but it works because of the way react-router
      works: It handles the requests from the front-end and routes users to
      other routes but overall, the React app is a single-page
    • If you want server-side React, consider using Next.js.

enter image description here

  1. You can put a designated error file error.html in the public folder of your React app and in the Static website hosting: Error document box, put in: error.html. This also works. I’ve tested it.
  2. Use AWS CloudFront > CloudFront Distributions > The distribution of your bucket > Error Pages and add the error code you want. If you don’t use react-router (like the example below), the bucket will respond with Error 403 so you can respond with your error.html.

enter image description here

  1. For TypeScript, currently react-router doesn’t support it so you should consider option 2 and 3. They’re going to update the library in future version 6.* according to this thread.

Method 3

In case this S3 bucket is served from a CloudFront distribution:

Create a custom error page (AWS Docs) in CloudFront distribution that routes 404 errors to index.html and returns 200 response code.
This way your application will handle the routing.

Custom Error Pages

enter image description here

Method 4

Redirecting 4xx to index.html would work, but that solution would make you fall into many other problems like google won’t be able to crawl these pages.
Please check this link, it solved this issue by using redirection rules for the S3 bucket.

Method 5

Case use Cloudfront to S3:

3b) AWS CloudFront — Error Pages
After creating the CloudFront distribution, while its status is In Progress, proceed to the Error Pages tab. Handle response codes 404 and 403 with Customize Error Response.

Google recommends 1 week or 604800 seconds of caching.
What we are doing here is to set up CloudFront to handle missing html pages, which typically occurs when a user enters an invalid path or, in particular, when they refresh a path other than the root path.

When that happens:

CloudFront will be looking for a file that does not exist in the S3 bucket; there is only 1 html file in the bucket and that is the index.html for the case of a Single Page Application like this project example
A 404 response will be returned and our custom error response setup will hijack it. We will return a 200 response code and the index.html page instead.
React router, which will be loaded along with the index.html file, will look at the url and render the correct page instead of the root path. This page will be cache for the duration of the TTL for all requests to the queried path.
Why do we need to handle 403 as well? It is because this response code, instead of 404, is returned by Amazon S3 for assets that are not present. For instance, a url of will be looking for a file called somewhere (without extension) that does not exist.

PS. It used to be returning 404, but it seems to be returning 403 now; either way it is best to handle both response codes).

Method 6

This question already has several great answers. Although the question is now old, I faced this problem today, so I feel that perhaps this answer can help.

S3 has two kinds of end points, and if you are facing this error, you have probably selected the S3 bucket directly as the endpoint in the Origin Domain Name field for your CloudFront Distribution.

This would look something like: and is a perfectly valid endpoint. In fact, it would seem that AWS does expect this as default behaviour; this entry will be in the drop-down list you have when you are creating your CloudFront distribution.

However, doing this and then setting error pages may or may not solve your problem.

S3 has, however, a dedicated Website Endpoint. This is accessible from your S3 Bucket > Properties > Static Website Hosting. (You will see the link on the top).

You should use this link instead of the original auto-populated link that comes up in CloudFront. You can put this in while creating your distribution, or after creation, you can edit from the tab Origins and Origins Groups, and then invalidating caches.

This link will look like:

Once this propagates and changes, this should then fix your problem.

tl;dr: Don’t use the default S3 endpoint in CloudFront. Use the S3 website endpoint instead. Your origin domain should look like this: and not like

More Information about website endpoints is available in the AWS Documentation here.

Method 7

Below configuration resolved
react router doesn't work in aws s3 bucket

Updated error pages in associated cloud-front distribution with custom error response from http status code 404-not-found to code 200-OK at response page path as / and near zero time-to-live

Method 8

There are some good answers in this thread mostly around configuring your S3 bucket with redirection rules. Although this would have also worked for my use case, which is typically hosting a React single page application using React Router through CloudFront with S3 configured as the Origin, I decided to try a different approach given a kind of new feature which was recently released, CloudFront Functions. The main idea is to rewrite the URL before it reaches CloudFront cache so that URLs created by React Router on the client side are rewritten to point to the application domain root.

You can read more about CloudFront Functions here:

The main idea is to use a CloudFront Function for rewriting the request Uri for any requests that are not targeting static files, i.e .js, .CSS, .html etc… and making it point to the /index.html file. This function would run as part of the viewer request trigger and run before CloudFront checks to see whether the requested object is in the CloudFront cache. That would result in any Uri sent by the client that is generated by the React Router to be rewritten before the content is requested from the origin server, thus not requiring to do any further redirects on S3. At the same time, files that are requested but do not exist anymore on the origin would return a 404 as expected, while all other static content would be served as requested.

Here is how my CloudFront function looks like:

function handler(event) {
  var request = event.request;

  if (!request.uri.includes('.')) {
    request.uri = "/index.html";
    request.querystring = {}

  return request;

You can make the URL re-writing rules as complex as you like, but surprisingly, this small piece of code works fine for my use case. Also, unlike [email protected] functions, CloudFront Functions are much more lightweight and allow you to experiment almost real time through AWS console by changing the code of your function there, testing it and deploying it within a couple seconds. However, they come with certain language limitations, so do not expect all JavaScript language features to be supported. This page documents what is available for you to use:

Method 9

This thread has been really helpful for me. I know this is old, but, I’d like to add a aws-cdk code snippet that finally worked for me. My use case needed me to utilize aws-cdk, so, I’m adding it here if you’re looking forward to a aws-cdk based solution.

The code snippet adds index.html as error page in s3 and also in cloudfront. For cloudfront specifically, I added the error page as /index.html for 404 and 403 with a response of 200

// imports...
// import * as cdk from '@aws-cdk/core';
// import * as s3 from '@aws-cdk/aws-s3';
// import * as cloudfront from '@aws-cdk/aws-cloudfront';

// following code snippet has to be added in a 
// class which extends to a Construct or a Stack

    const bucket = new s3.Bucket(this, '<Specify a name>', {
      bucketName: '<add your bucket name>',
      websiteIndexDocument: 'index.html',
      websiteErrorDocument: 'index.html',
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // edit it according to your use case, I'll change it though
      autoDeleteObjects: true,

    const cloudFrontOAI = new cloudfront.OriginAccessIdentity(this, 'OAI');

    const distribution = new cloudfront.CloudFrontWebDistribution(this, props?.stackName + 'AbcWebsiteDistribution', {
      defaultRootObject: "index.html",
      errorConfigurations: [{
        errorCode: 404,
        responsePagePath: "/index.html",
        responseCode: 200
      }, {
        errorCode: 403,
        responsePagePath: "/index.html",
        responseCode: 200
      originConfigs: [
          s3OriginSource: {
            s3BucketSource: bucket,
            originAccessIdentity: cloudFrontOAI,
            originPath: "/artifacts" // change this based on your use case
          behaviors: [{ isDefaultBehavior: true }]


Answers that really helped me from this thread are:

Method 10

Why it happens

Your issue is that you want to pass responsibility to routing to your react app/javascript. The link will work because react can listen to the link click and simply update the route in the browser URL bar. However, if you go a location where your script (index.html and the bundle.js or wherever your app code is located) is not loaded, then the JavaScript is never loaded and has no chance to handle the request. Instead, whatever runs your server will take care of the request, look if there is a resource at this place, and return a 404 error or whatever else it found.

The solution

As mentioned in the comments, this is the reason why you need to redirect 404-errors to the exact location where your app is placed. This is nothing specific to Amazon, it is a general requirement to set up your react app.

To solve the issue you need to find out what handles routing on your server and how to configure it. For example if you have an Apache server, an .htaccess file could take care of the matter like this:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]

This file lets the server redirect not found errors to the index.html. Keep in mind that it might affect other routing rules that are on your server, so this configuration is easiest, if your react app has a place on its own, not interfering with other stuff.

Method 11

I ran into this issue despite using the configuration as described in the accepted answer, but if you still get 403 errors, AND you’re using AWS Cloudflare Manager, you can create an error page in the “Error Pages” tab of the distribution settings, with the following configuration:

Error Code: 404,
Customize Error Response: Yes, 
Response Page Path: /index.html, 
HTTP Response Code: 200

Pic of Error Page Config to pass route handling to browser router

This worked for me although I’m by no means knowledgeable about proper server admin, hopefully someone will tell me if this is a major issue, if serendipitous.

Method 12

update config of s3 with hosting static and default with index.html
enter image description here

add CloudFront with error page

404 error-> index.html->status 200

enter image description here

Method 13

100% Working and Tested to host SPA website like React in S3 Bucket
and fix 404 sub pages issue

  1. Login to amazon console
  2. search for Cloud front
  3. Select your Distributions
  4. Select Error Pages tab and Click on Create custom Error Response
    react router doesn't work in aws s3 bucket
  5. Create a response rule here
    react router doesn't work in aws s3 bucket
  6. Save error rule

You have Done!

Method 14

I fixed it by using HashRouter instead of Router

import { Switch, Route, HashRouter } from 'react-router-dom';

const App = () => (
                <Route exact path="/" component={Home}/>
                <Route exact path="/projects" component={Projects}/>
                <Route exact path="/about" component={About}/>
                <Route component={NoMatch}/>

The app is acceccible by something like

on localhost like

No changes in S3 were needed.

Method 15

I’m using NextJS and had the same issue.
My solution was to check the routing information and push if it doesn’t fit.

const router = useRouter();
useEffect(() => {
    if (!router.pathname.startsWith(router.asPath)) {

Nothing needed to be modified at the S3 side except from routing the error page to index.html

Method 16

As per previous answers, the best way to solve the problem: redefine the fallback strategy.
If you’d like to learn more about client-side routing vs server-side, and particularly about different routing approaches in the React JS, check out this article.

All methods was sourced from or, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Notify of

Inline Feedbacks
View all comments
Would love your thoughts, please comment.x