Skip to content

React-Router locations on AWS s3 bucket do not work

I have my static react app hosted in an AWS s3 bucket. It renders fine. However, I am utilizing react-router-dom to navigate between “pages.” (I have enabled public access, enabled static website hosting, listed my index document as index.html). For example, my bucket website endpoint is http://< When I click on one of my navigation buttons which links to http://<, it either redirects me to the index page or gives me a 403 error (depending on if I use the bucket website endpoint or the index.html object url). Either way, the point is that I cannot access any url path that is more specific than the very top layer of my home page. It is not letting me route to a more specific location / different page. I have looked into this quite a bit and am pulling my hair out. Redirection rules do not seem like an acceptable answer. My reasoning is that if I am going to get a 403 error every time I try to access a different page, redirecting back to index is not a viable solution. I want to allow public access to ALL URL LOCATION so that I can access any location from the initial endpoint. How can I fix this?


You have a couple of options.

Option #1:

The simplest is using HashRouter instead of BrowserRouter. This will make changing paths look like http://< (note the #). As the hash is only processed by the browser, it doesn’t change the request path to the server. Note that this solves the problem for any static host, not just s3, so also works for e.g. github-pages.

Option #2:

If you don’t want to have your paths like that, there is a mildly hacky solution. In the bucket settings under “Redirection Rules”, you can add something like:

    "Condition": {
      "HttpErrorCodeReturnedEquals": "403"
    "Redirect": {
      "ReplaceKeyPrefixWith": "#/"

This says, “any time you get a request for a non-existent path, replace /path with /#/path. Now it’s serving the main page with a hash parameter.

Finally, we want to remove the hash from the url, so in the root index.js, BEFORE rendering the <App />, you can do something like:

import { createBrowserHistory } from 'history'

const replaceHashPath = () => {
  const history = createBrowserHistory()
  const hash = history.location.hash
  if (hash) {
    const path = hash.replace(/^#/, '')
    if (path) {

This runs before anything is actually rendered, replaces the URL in the browser history with the non-hashed version, Now everything will work as expected.

Read more about redirection rules here: