I have experienced similar issues trying to run a react.js app through cloudront, here are a few things you should check:
- Make sure that your s3 hosting bucket has public read access, or that you have set up your cloudfront origin access policy with the s3 bucket by first creating an origin access identity in cloudfront, then associating the identity with your s3 bucket through the access policy:
{
"Version": "2012-10-17",
"Id": "MyPolicy",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity YOUR_CLOUDFRONT_ORIGIN_ACCESS_ID"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR_S3_BUCKET/*"
}
]
}
- Make sure your s3 bucket has a cors policy allowing requests from any domain similar to:
*
GET
3000
Authorization
Content-Length
- If your package.json file in the react.js app has a homepage, you should remove it as this the application expects to be hosted from that url and not the cloudfront domain. Example package.json with homepage:
{
"name": "your-package-name",
"version": "1.0.0",
"description": "",
"author": "",
"homepage": "https://somepage.com/home",
"repository": {},
"dependencies": {}
}
- Make sure that your root object in the cloudfront distribution is index.html
- Make sure that after you run "npm run build" in the react app, you are only syncing your build folder with the s3 bucket.
- Make sure that if you use the aws cli to move your build files to the s3 bucket, you use the command "aws s3 sync build/ s3://YOUR_DEPLOY_BUCKET/ --acl public-read --delete", this ensures that only the most recent build files are uploaded to s3, and that any old files are deleted.
- After pushing files to s3, it is a good idea to run a cloudfront invalidation to ensure that your old files are removed from the cache and any new requests serve up the new files. you can run "aws cloudfront create-invalidation --distribution-id YOUR_CF_DISTRIBUTION_ID --paths '/*'" to accomplish this.
- Because react.js uses the virtual dom, and the only html document actually served is the index.html document, you need to add custom error responses to your cloudfront distribution as cloudfront will expect to serve up web pages like about.html or contact.html depending on the route in your react app. I recommend adding custom error responses for http codes 400-404 and mapping the response to status code 200 with a redirect to the "/" route. This ensures that if cloudfront cant find a file such as about.html, it will stay on the index.html file (where your react app is) and react router will virtually route to your /about route. see below for example configuration: