I deployed my React website build/
folder into an AWS S3 bucket.
If I go to www.mywebsite.com
, it works and if I click on a
ta
I fixed it by using HashRouter
instead of Router
import { Switch, Route, HashRouter } from 'react-router-dom';
const App = () => (
<HashRouter>
<div>
<NavBar/>
<Switch>
<Route exact path="/" component={Home}/>
<Route exact path="/projects" component={Projects}/>
<Route exact path="/about" component={About}/>
<Route component={NoMatch}/>
</Switch>
</div>
</HashRouter>
);
The app is acceccible by something like
https://your-domain.s3.amazonaws.com/index.html?someParam=foo&otherPram=bar/#/projects
on localhost like
https://localhost:3000/?someParam=foo&otherPram=bar/#/projects
No changes in S3 were needed.
Case use Cloudfront to S3:
https://hackernoon.com/hosting-static-react-websites-on-aws-s3-cloudfront-with-ssl-924e5c134455
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 https://yourdomain.com/somewhere 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).
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.
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]
</IfModule>
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.
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: bucket-name.s3.amazonaws.com
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: bucket-name.s3-website.region-name.amazonaws.com
.
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: my-application.s3-website.us-east-1.amazonaws.com
and not like my-application.s3.amazonaws.com
.
More Information about website endpoints is available in the AWS Documentation here.
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:
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
application.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.
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.
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.