I have a react app deployed to Amazon S3 and distributed by Cloudfront, and it is fully working as expected right now.
However, when I open the browser console, it shows a 404 error.
For example, let's say the address for my website is https://shinhong.com.
Type https://shinhong.com in the address bar and hit Enter: Works without errors
Now click some link on the website and navigate to https://shinhong.com/path/to/something: Works without errors
Now refresh the current page (https://shinhong.com/path/to/something): Works, but shows 404 error like GET https://shinhong.com/path/to/something 404 (Not Found)
Or type https://shinhong.com/path/to/something in the address bar and hit Enter: Works, but shows the same error as above
The network request with 404 error had the following response headers:
x-amz-cf-pop: ICN51-C1
x-amz-error-code: NoSuchKey
x-amz-error-detail-key: admin/logs
x-amz-error-message: The specified key does not exist.
x-cache: Error from cloudfront
but with response:
<!doctype html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8"/>
<link rel="shortcut icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#2c7be5"/>
<link rel="manifest" href="/manifest.json"/>
...
which is the normal public/index.html file.
I have checked this link CloudFront + S3 Website: "The specified key does not exist" when an implicit index document should be displayed, but no luck.
Also, everything is the same when I access via Cloudfront domain https://dq9*****u4da.cloudfront.net or S3 bucket domain mys3bucket.s3-website.ap-northeast-2.amazonaws.com. There is no error in my dev server btw.
Can you suggest any possible cause for this weird problem?
Thank you.
I have the same configuration: Amazon S3 + Cloudfront React app
I think I solved it in Cloudfront
I changed the HTTP Responde Code from 400 to 200
Now If I go to https://example.com/signup, I dont have any 400 anymore
My problem is Crawl in Google Search Console can't found sub-routes in React.
The URL is https://huynhsamha.github.io/crypto, and crawler can fetch and render homepage (route /) and static files such as /robots.txt, /favicon.ico, but it can't found the sub-routes, which are rendered by React, (SPA, using Redux), such as /algorithm/sha256. Example, https://huynhsamha.github.io/crypto/algorithm/sha256 can't found by Crawler but it can be accessible.
This is my screenshot in Google Search Console I've tried.
Who can explain why and how to fix my problem? I'm using react-router-dom with react-redux My repository on github here
Edit 1
I've also tried the answer https://stackoverflow.com/a/53966338/8828489 in this question, but not working. I've added script in index.html (https://github.com/huynhsamha/crypto/blob/gh-pages/index.html), but search console can't still found, so it also can't render any error on screen.
Edit 2
I've also tried the answers https://stackoverflow.com/a/54040745/8828489 and https://stackoverflow.com/a/54048119/8828489 in this question, but not working. I've created 404.html file and add scripts as the answer instructs but it didn't also work.
Edit 3
I've also tried the answer https://stackoverflow.com/a/54044148/8828489 in this question by creating a simple sitemap.xml, googlebot can find this file and discover all URLs I defined in sitemap. But it also cannot fetch and render URLs mentioned.
I found that when i opened https://huynhsamha.github.io/crypto/algorithm/sha256, I actually received a 404 as a response. I think your workaround for hosting SPA on GitHub using the 404.html is the issue here. While us humans see your app being served on our browser correctly, googlebot doesn't care and just look at the response code and see that it has received a 404. You'll need a different workaround that doesn't involves using the 404.html as the entry point to your app directly.
Try following this workaround by rafrex instead, it redirects the browser to index.html using the 404.html while keeping the original route, it claims that googlebot register that as a 301 instead of a 404, for your case that means adding these changes below to your site, pay attention to the script below the <!-- ------Single Page Apps GitHub Pages Workaround------ -->:
<!-- 404.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cryptography</title>
<!-- ------Single Page Apps GitHub Pages Workaround------ -->
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
// ----------------------------------------------------------------------
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes
// http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set segmentCount to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
// https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe
// Otherwise, leave segmentCount as 0.
var segmentCount = 1;
var l = window.location;
l.replace(
l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +
l.pathname.split('/').slice(0, 1 + segmentCount).join('/') + '/?p=/' +
l.pathname.slice(1).split('/').slice(segmentCount).join('/').replace(/&/g, '~and~') +
(l.search ? '&q=' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
);
</script>
</head>
<body>
</body>
</html>
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta name="description" content="Cryptography Algorithms: Secure Hash Algorithm (sha256, sha512, ...), Message Digest Algorithm (md5, ripemd160), HMAC-SHA, HMAC-MD, pbkdf2, Advanced Encryption Standard (AES), Triple Data Encryption Standard, (TripleDES, DES), RC4, Rabbit, ...">
<meta name="keywords" content="crypto, algorithms, secure hash, sha, sha512, sha256, message digest, md5, hmac-sha, aes, des, tripledes, pbkdf2, rc4, rabbit, encryption, descryption">
<meta name="author" content="huynhsamha">
<!-- Open Graph -->
<meta property="fb:app_id" content="440168923127908">
<meta property="og:url" content="https://huynhsamha.github.io/crypto">
<meta property="og:title" content="Cryptography Algorithms">
<meta property="og:description" content="Cryptography Algorithms: Secure Hash Algorithm (sha256, sha512, ...), Message Digest Algorithm (md5, ripemd160), HMAC-SHA, HMAC-MD, pbkdf2, Advanced Encryption Standard (AES), Triple Data Encryption Standard, (TripleDES, DES), RC4, Rabbit, ...">
<meta property="og:type" content="website">
<meta property="og:image" content="%PUBLIC_URL%/img/main.jpeg">
<meta property="og:site_name" content="Cryptography">
<meta property="og:locale" content="vi_VN">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary">
<meta name="twitter:site" content="#huynhsamha">
<meta name="twitter:creator" content="#huynhsamha">
<meta name="twitter:url" content="https://huynhsamha.github.io/crypto">
<meta name="twitter:title" content="Cryptography Algorithms">
<meta name="twitter:description" content="Cryptography Algorithms: Secure Hash Algorithm (sha256, sha512, ...), Message Digest Algorithm (md5, ripemd160), HMAC-SHA, HMAC-MD, pbkdf2, Advanced Encryption Standard (AES), Triple Data Encryption Standard, (TripleDES, DES), RC4, Rabbit, ...">
<meta name="twitter:image:src" content="%PUBLIC_URL%/img/main.jpeg">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="author" href="//github.com/huynhsamha">
<link rel="canonical" href="//huynhsamha.github.io/crypto">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link href="//fonts.googleapis.com/css?family=Open+Sans:400,600,700&subset=vietnamese" rel="stylesheet">
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.min.css">
<link rel="stylesheet" href="%PUBLIC_URL%/lib/font-awesome/css/font-awesome.min.css">
<!-- ------Single Page Apps GitHub Pages Workaround------ -->
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// https://github.com/rafrex/spa-github-pages
// Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
// ----------------------------------------------------------------------
// This script checks to see if a redirect is present in the query string
// and converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(function(l) {
if (l.search) {
var q = {};
l.search.slice(1).split('&').forEach(function(v) {
var a = v.split('=');
q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
});
if (q.p !== undefined) {
window.history.replaceState(null, null,
l.pathname.slice(0, -1) + (q.p || '') +
(q.q ? ('?' + q.q) : '') +
l.hash
);
}
}
}(window.location))
</script>
<title>Cryptography</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script src="%PUBLIC_URL%/js/jquery-3.3.1.slim.min.js" type="text/javascript"></script>
<script src="%PUBLIC_URL%/js/popper.min.js" type="text/javascript"></script>
<script src="%PUBLIC_URL%/js/bootstrap.min.js" type="text/javascript"></script>
<!-- Google Adsense -->
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
</body>
</html>
More info and discussions on GitHub's support for single page app here.
I poked around in your source code and don't see anything alarming; however, I found a few posts about similar issues (1) (2). The second seems particularly helpful, so I'll repeat it here. Shout out to #Zerotorescue on Reddit.
Open Google Search Console and go to Crawl -> Fetch as Google and do a fetch and render.
Add this to your site, either as a part of tag in your HTML file or as part of the bundle:
https://gist.github.com/mstijak/715fa2dd3f495a98386c3ebbadbabb8c
I recommend the former since that makes it easier to change if you need to make it more readable (no need to recompile your app).
Push this to your site and then do another fetch and display. The error preventing Google from running your app will now show. The search console resolution is pretty low so you may have to increase the font-size of the error and fetch again. Don't worry, Google doesn't mind repeated calls.
You'll probably find that Google's crawler can't process your code because you're using some ES6 feature it doesn't support. You can fix this by polyfilling. I've tried a couple of things such as https://polyfill.io/ which turned out to not really support Googlebot and while it might sometimes work, it is pretty unreliable. Instead I recommend using babel-polyfill. It will increase your bundle size a little bit for everyone but in my experience it provides the widest browser support with a minimal headache. Just turn it on and you're done.
If you're using create-react-app this is the polyfills.js file I use that you could copy:
https://github.com/WoWAnalyzer/WoWAnalyzer/blob/2c67a970f8bd9026fa816d31201c42eb860fe2a3/config/polyfills.js#L1
Notice there are a lot of comments explaining all the issues the polyfill service introduce that you won't have to deal with if you use babel-polyfill.
Because, react app is onepage web, You need a sitemap file, you can find it how to make a one here ,too make a 404 page, and every route add property that has a anchor
like to
<a title="This my Route One" href="https://myreactapp/routeOne" alt="Route One"/>
The problem is that you're using a 404 page to capture incoming traffic to routes other than /. This means those routes serve a 404 status code (you can see this if you open Network in dev tools and try to visit one of those deep URLs). Google sees a 404 status in the response header and just gives up right away. You probably noticed that the "Not Found" message in Webmaster Tools popped up super-fast.
On a normal server, you would capture those routes and return a successful status code like 200 or 301 and Google would continue crawling. However, because you're using GitHub pages, you need to hack your way around it.
You should be able to do this by setting up an instant redirect from that 404 template to your index template. Browsers interpret instant redirects as 301s. To do this, replace the contents of your 404.html with something like this:
<html>
<head>
<script>
sessionStorage.redirect = location.href; // we'll use this later
</script>
<meta http-equiv="refresh" content="0;URL='/crypto'">
</head>
<body></body>
</html>
Just make sure the file-size of that 404.html is greater than 512b or IE will discard it (damn M$...).
Lastly, you'll need to make sure your index.html captures the original route. To do so, use a script like this in the head of your index.html:
<script>
(function(){
var redirect = sessionStorage.redirect; // remember me?
delete sessionStorage.redirect;
if (redirect && redirect != location.href) {
history.replaceState(null, null, redirect);
}
})();
</script>
For reference, I stole this clever hack from:
https://www.smashingmagazine.com/2016/08/sghpa-single-page-app-hack-github-pages/
I also, do not see anything alarming in your code (although I don't think you need the baseUrl in your <Route /> - though I could be wrong, and don't think that's the issue, but it may be worth eliminating if unnecessary).
Just a guess but looking at the networks tab as I bounced around the links, I noticed the service worker. I am, admittedly, not super savvy when it comes to service workers (yet!), however googling a bit revealed that google crawlers do not yet support service workers as asserted in this article, this article, and by google.... I also noticed that if I run a Lighthouse test on one of the links I reached via in-app navigation (for instance I click on the /algorithm tab from the nav on the homepage and then run a Lighthouse test) I get the following errors:
There were issues affecting this run of Lighthouse: Chrome extensions
negatively affected this page's load performance. Try auditing the
page in incognito mode or from a Chrome profile without extensions.
and more interesting:
Lighthouse was unable to reliably load the page you requested. Make
sure you are testing the correct URL and that the server is properly
responding to all requests. Status code: 404.
...despite clearly seeing it rendered in the browser. Seems suspect. So, if that is part of how navigation is happening (seems it likely is based on the registerServiceWorker.js file in your repo lol), it may be the cause of your links not being found/followed.
Before i start the question, here is what i already know to avoid answers along the same lines.
TL;DR: I already know I can use a webserver and serve the index.html as http://localhost:8081/index.html and it will work.
Now for the question details:
I have created a minimal react-js app, referencing babel-standalone in the index.html file as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Minimal</title>
</head>
<body>
<div id='divRootComponent'></div>
<!-- react reasonably latest for oct/2018 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>
<!-- babel -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<!-- react CUSTOM component.. i.e. myApp's entry point -->
<script type="text/babel" src="index.js"></script>
</body>
</html>
and the index.js contents are:
class YoSupComponent extends React.Component {
constructor () {
super();
this.state = {
message: 'whatever...'
};
}
componentDidMount() {
console.log('YoSupComponent.componentDidMount');
}
render () {
return (
<div>
Yo ! Dude or Dudette... sup ?
</div>
);
}
}
const props = {}; //none for now...
ReactDOM.render(<YoSupComponent {...props} />
, document.getElementById('divRootComponent'))
When I tried to access the index.html file in the Browser via file:///path/to/index.html, the error is:
Access to XMLHttpRequest at 'file:///D:/path/to/index.js' from origin
'null' has been blocked by CORS policy: Cross origin requests are
only supported for protocol schemes: http, data, chrome,
chrome-extension, https. # babel.min.js:24
So thinking the problem is related to the script tags referencing files remotely, I download react and babel locally, and make references local; then again I access file:///path/to/index.html.
Still get the same error ! whats going on? a) why does babel even use XMLHttpRequest (as per the error message) when the babel.js file is now local ? b) why no such message for react files ?
According to MDN, if you specify a script tag with a type that's not text/javascript, it will be ignored by the browser:
The embedded content is treated as a data block which won't be processed by the browser. Developers must use a valid MIME type that is not a JavaScript MIME type to denote data blocks. The src attribute will be ignored.
In other words, the browser does not load or run index.js in your example. This makes sense - if it did, you'd get a syntax error, as your browser can't understand JSX.
What actually happens is that the babel-standalone script looks at your HTML, finds all of the script tags marked text/babel, loads them via XMLHttpRequest, and then compiles and runs them.
This is why you're getting CORS errors - the browser's not loading the file, the script is. Unfortunately, I don't think you can resolve this without using a web server or compiling your Babel code ahead of time.
My app works great in the dev environment, where I use proxies. After I pushed to heroku (production), I saw a blank screen. In Chrome Developer Tools, I saw the following two errors:
Unexpected token. manifest.json:1
Uncaught SyntaxError: Unexpected token < main.bac17543.js:1
When i click on these two errors, I saw that the manifest file, the main.css file, and the main.css file all have the HTML file as the source. It fails to load the other files from the client/build/index.html.
Okay, so experimenting with this, I found when I run "npm run dev," it works great on the client port. However, there's an error when the app starts on the server port and serves the index.html file with the following express code shown in the course:
if (process.env.NODE_ENV === 'production') {
//Express will serve up production assets like main.js file
app.use(express.static('/client/build'));
//Express will serve up html file if it doesn't recognize the route
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'))
});
}
This code successfully serves up the html file. But all other files linked within fail.
After several reloads to Heroku, I reproduced the error by taking out the production if statement and navigating to localhost:5000 (sever port). After running "npm run build" from the client folder, I received the same errors when "npm run dev" is run. Even if i switch "build" to "public" to run index.html before "npm run build," I receive the same errors. To test if the html file was called, I wrote <h3>testing html</h3>, and the HTML successfully rendered to the screen as the only content. But it seems that manifest, favicon, css files, and javascript files were not found, returning the original index.html file and throwing an error at the beginning of the file: "<."
Here is my index.html file under the build folder:
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name="theme-color" content="#000000"><link rel="manifest" href="./manifest.json"><link rel="shortcut icon" href="./favicon.ico"><title>Compass</title><link href="./static/css/main.c092d6b6.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script type="text/javascript" src="./static/js/main.bac17543.js"></script></body></html>
And here it is in the public folder:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<h3>this HTML is called, but the javascript file fails when served from Express</h3>
<div id="root"></div>
</body>
</html>
I've tried both absolute and relative paths in the href and src attributes in both folders. Yet still no dice. I'm pretty confident the paths are right because they work otherwise, but for some reason it's not reading correctly. The build paths are server/build/index.html, sever/build/static/css/main.d3f5fbbb.css, and server/build/static/js/main.69f468ec.js.
Perhaps there is some configuration issue? I haven't touched configuration for webpack or anything. I'm just not sure why the buck stops with the HTML file when serving from express. Can you please advise?
I had this same exact issue. I was able to solve the problem by setting my homepage in my react package.json from the github page that was auto filled to the heroku url since that was what was serving the static files.
Found this info to be helpful and led me to checking homepage property in my package.json
https://github.com/facebook/create-react-app/issues/527
Hope this helps or someone else that is trying to deploy express and react from one repo to heroku.
This questions is pretty specific to the react-starter-kit, though, someone else may be able to help here on the internets. I am trying to use the react-starter-kit with a flask application I am developing. I chose this yeoman generator as it had a lot of things I am looking for, though, I don't want to run on a node server. I have a flask app that I would just like to wire up to the react front end. I can't quite figure out how the node routes know which .js bundled file to include (and Node routes are a bit confounding to me anyways). Where would be the best place to start here?
I think if I could start with a specific question, to what .js file could I have an index.html point to? Or how can I find that?
I am tentative to ask this question here, but I hopefully will be directed to the right place.
Here is my index.html:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Flask React</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- styles -->
</head>
<body>
<div id="app"></div>
<!-- scripts -->
<script type="text/js" src="build/assets.js"></script>
</body>
</html>
Here is my flask view:
#CORE.route('/', methods=['GET'])
def index():
return render_template('index.html')
Here is the build/assets.js
module.exports = {"main":{"js":"/main.js?032d72b634c91d2f8756"}};
Where the /main.js?{{string}} points the that bundled version of the app.
Here is the file directory layout:
App
|-app
|-Core
|-view.py
|-Templates
|-index.html
|-build
|-assets.js
|-src
What I have decided to do, and what I would recommend doing, is to use Graphene and Flask-GraphQL alongside the react-starter-kit. Then to get to my data I can just have the node server query to the flask server to get the data from postgres. This will allow me to still to data-y things in python with the flask server and produce a RESTful API if needed, but will allow me to benefit from the awesomeness of GraphQL.
I am still getting everything figured out, and I will come back to repost in the future when I get things working all the way.