So I have a create-react-app bootstrapped application that is being served by express. I've noticed that for some of my users their browser is caching the index.html and as a result my application is making requests for non-existent js and css assets.
To prevent this from happening, I've tried setting the cache control header on the express static server to be "no-cache"..which did not work. I've also tried forcing the ETAG to be false so the browser is forced to take back a new index.html, but this hasn't worked either.
This is how I am serving my application
app.get('/*', forceHttps)
// root (/) should always serve our server rendered page
// other static resources should be served as they are
const root = path.resolve(__dirname, '..', 'build')
app.use(
express.static(root, {
maxAge: '1d',
etag: false,
setHeaders: (res, requestedFilePath) => {
if (requestedFilePath.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache')
}
},
})
)
app.get('/*', function(request, res, next) {
res.sendFile(path.join(__dirname, '..', 'build', 'index.html'))
})
Why do I run into this issue at all? After every build index.html changes and the ETAG should change because the index.html is different.
Why isn't my current solution working? I'm forcing there to be no cache
How do I solve this problem? I have existing users that have a stale index.html and I need to cache bust.
Thanks!
Related
I've made spa site and compressed it using brotli, but how to serve these files?
When I'm trying to code like
app.use(express.static(path.join(__dirname, "../", "prerendered")));
app.use(function (req, res) {
res.sendFile(path.join(__dirname, "../", "prerendered", "index.html.br"));
res.set("Content-Encoding", "brotli");
});
it's not working...
What I'm doing wrong?
I cannot access the routes that I have set up in my app when running from the build version. I can access them on dev enviroment though when my react app runs at a different port than the server.
I have included the below in my express server in order for it to serve the react app and only the root page is showing.
app.use(express.static(path.join(__dirname, 'build')))
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'))
})
React Router does all the routing in the browser, so you need to make sure that you send the index.html file to your users for every route.
This should be all you need:
app.use('/static', express.static(path.join(__dirname, '../client/build//static')));
app.get('*', function(req, res) {
res.sendFile('index.html', {root: path.join(__dirname, '../../client/build/')});
});
I found the solution of my issue.
I was having the HTML anchor tags instead of the Link attribute of react-router to my code.
Replaced them accordingly and my app is operating normaly.
I have a React app running successfully locally and all api requests are successfully running from a separate server.
When I run a build, the path to the api server is lost and no data is loaded.
Below are a few screenshots...
Loading data successfully from api.
Pointing IIS to react /build folder using localhost:80. No data loading.
Here is an example of an api call in my node/express server/index.js file
app.get('/api/company', (req, res) => {
api_helper.GET('https://****/api/company')
.then(response => {
res.json(response)
})
.catch(error => {
res.send(error)
})
})
My package.json file has the url of the express proxy (running in the background).
"proxy": "http://localhost:5000/",
My question is, why isnt the api loading in production /build? I just get this...
Request URL: http://localhost/api/site
Request Method: GET
Status Code: 404 Not Found
Remote Address: [::1]:80
Referrer Policy: no-referrer-when-downgrade
but when just running locally (npm start) I get this and data loads from api.
Request URL: http://localhost:3000/api/site
Request Method: GET
Status Code: 304 Not Modified
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade
Any help appreciated, driving me mad! Thanks.
After much testing I discovered, you must put the routes before
Wrong Example:
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.use('/', routes);
Right Example:
app.use('/api', routes);
app.use(express.static(path.join(__dirname, 'build')));
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
For anyone else struggling with this, I figured it out..
I had to add this to my express server.js file in the root folder of my project.
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
I then pointed to the address where express is running, in my case http://localhost:5000
This worked.
I also then set up a rewrite rule in IIS to point localhost and our domain name to localhost:5000
All working now, hope it helps someone else.
Thanks for your info. I am quite new to ReactJS and I also encountered similar problems when I created my production build. Actually I had added similar things like
app.use(express.static(<build_folder_dir>));
in my Express Server before then I came to search and see your post. Anyway, I did not add something like the second line of your code and my API calls are written in router created in a separate js file.
app.use('/api/some_path', <imported_router>);
In the exported router object, codes are written like this:
router.get('/some_sub-path')
To make API calls, I used axios in my react app
axios.get(
"/api/some_path"+"/sub-path?param_1="+<param_value>,
{
headers:{
"Content-Type":"application/json",
<some headers>
}
}
).then((res)=>{<Some codes using the res.data.<any param in body>>})
Finally,I added these lines in the server.js
app.get('/*', function(req, res) {
res.sendFile(path.join(__dirname, <path of the index.html in the build dir>), function(err) {
if (err) {
res.status(500).send(err)
}
})
})
Yet, I made a stupid mistake that my app crashed because the app.get overwrite the settings in router. Just a reminder, if you enable any API calls in GET method, use regex to exclude the pattern for making API calls.
const express = require('express');
const path = require('path');
const app = express();
app.listen(9000);
app.get('/test', function(req, res) {
console.log("not being hit");
res.send(200);
});
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function (req, res) {
console.log("always hits");
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
This seems like such a simple problem but my brain is starting to turn to mush.
Here's the details:
I have run build on a react app and the index.html file resides in the build folder, and I want this served via express.
I want express to prioritize /test first, and if it's not /test, then I want it to serve the index.html file in the build folder.
If you go to /test, it is skipped and always hits the /* route. If you remove the wild card and use / instead, neither routes will hit if you go to / or /test in the browser. However, index.html is still served and it looks like that's because of the static middleware.
Everything I have read suggests that order in express is important, but I feel like I'm losing my damn mind and I'm starting to slowly descend into madness.
Thanks in advance.
I've deployed a single-page app using a frontend framework to my Parse Hosting.
However, there's a huge issue in there: sub-paths get routed to the /public folder, and there's only an index.html and a bunch of assets in there.
I've tried numerous options on serving that static index file through all other routes, by using Express or HTTP in cloud/main.js, but it seems Parse runs a custom subset of Node modules. They've erased all filesystem methods. There's no sendFile() on Express API, no readFile()on fs module...
What can I do to achieve that?? I just need all paths not in the public folder to serve the same thing: my index.html file.
What I've already tried:
Read the file and serve it:
app.use(function(req, res) {
fs.readFile('../public/index.html', 'utf8' , function(err, data) {
if (err) {
console.error(err);
}
res.send(data);
});
});
Serve it as an Express Middleware (probably the most efficient way):
app.use(function(req, res) {
res.sendFile('../public/index.html');
});
Serve it as a catch-all route:
app.get('*', function(req, res) {
res.sendfile('../public/index.html');
});