Reputation: 103
I have a nodejs10 application that is deployed to Google App Engine. If a user has previously used the app in Chrome, the app does not work when the app is redeployed. The app is created with create-react-app and built with webpack so css and js are renamed on build but the index.html that references those files is not. So when the user access the site after deploy it will get the cached index.html that references js/css-files that does not exist anymore. This in turn causes Uncaught syntax error: unexpected token '<'
The only way I have found to get the app working again is to open inspector, disable cache and reload the page. Needless to say, this is not something you can ask the end user to do.
I have searched for a solution for this and found similar problems but not exactly the same, or if they are the same there is no solution available.
The express code serves index.html with res.sendFile
when the request is not made to an existing static file to make React router work properly:
app.get('*', (req,res) =>{
try {
res.sendFile(path.join(public+'/index.html'))
} catch(e) {
res.status(404).send()
}
});
The thing is I recently moved the app from a personal google cloud account to my business account, and I have never had this problem before. So either I have changed something to cause this, or App Engine has changed.
The response headers (response code 304) looks like:
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Content-Encoding: gzip
Content-Length: 1200
Content-Type: text/html; charset=UTF-8
Date: Thu, 03 Sep 2020 20:43:51 GMT
ETag: W/"967-49773873e8"
Last-Modified: Tue, 01 Jan 1980 00:00:01 GMT
Server: Google Frontend
Vary: Accept-Encoding
X-Cloud-Trace-Context: 7f98678c2ba7dc90ad204b67bec766df;o=1
X-Powered-By: Express
So as you can see the "Last-modified" date on the file is Jan 1, 1980. And I have verified that the file actually has that last modified date on app engine. But in the local build folder that is deployed, the file has a current date. Don't know if this is what is causing the problem though.
So I'm kind of lost. I guess I could hack the "last modified" header, but that does not feel like like it's a good solution. And I am also concerned the the same behavior could happen on other files that will cause similar problems. Anybody else run into this that came up with a solution?
Upvotes: 3
Views: 2031
Reputation: 11
As mentioned in the original question, the etag on the index.html is:
W/"967-49773873e8"
I recognized that etag, one example of an etag on my appengine + express website is W/"bd1-49773873e8"
After that I noticed that all my etags ended in the same hash: 49773873e8
This is supposed to be a hash of the body of the file being sent.
It seems like something is going wrong in either express or app engine, causing the hash to always end with 49773873e8
The first part is derived from the length of the file, so if 2 files (or the old index.html) are of equal length there is a hash collision.
Upvotes: 1
Reputation: 519
people.
After struggling I found the right solution.
When serving static files from express, normal middleware don't apply. You need to use the custom function setHeaders
from express.static.
So we need to cover both cases.
Here is the code:
app.use(express.static('build', { etag: false, lastModified: false, setHeaders: (res, path) => {
// No cache for index html otherwhise there's gonna be problems loading the scripts
if (path.indexOf('index.html') !== -1) {
res.set('Cache-Control', 'no-store')
}
} }));
app.set('etag', false)
app.disable('x-powered-by');
app.use((req, res, next) => {
// hide x-powered-by for security reasons
res.set( 'X-Powered-By', 'some server' );
// This should apply to other routes
res.set('Cache-Control', 'no-store')
next()
})
app.get('/', (req,res) =>{
// I think this is redundant
res.set('Cache-Control', 'no-store')
res.sendFile(path.join(__dirname+'/build/index.html'), { etag: false, lastModified: false });
});
app.get('*', (req,res) =>{
// I think this is redundant
res.set('Cache-Control', 'no-store')
res.sendFile(path.join(__dirname+'/build/index.html'), { etag: false, lastModified: false });
});
const port = process.env.PORT || 8080
app.listen(port, () => console.log('App listening on ' + port));
Upvotes: 0
Reputation: 721
I had exactly the same problem with the weird timestamps.
It turns out to be behaviour by design as all timestamps are made zero on deploy by app-engine. see: https://issuetracker.google.com/issues/168399701
I solved it in express like this:
app.use("/", express.static(root, { etag: false, lastModified: false }));
Or for individual files:
app.get("/*", async (req, res) => {
return res.sendFile("index.html", { root, lastModified: false, etag: false });
});
Upvotes: 4
Reputation: 1
I suspect that you're also calling app.use(express.static())
with the directory containing index.html
. Move your template files to a separate directory from your static content.
Upvotes: 0
Reputation: 4660
As mentioned in the comments, you won't be to clear a client-side browser cache. So, this wouldn't be a viable solution for you. However, considering the fact that everything works fine, but only the caching issue, I have searched and have found a possible alternative.
It seems that this repository here, called nocache
, it's a middleware to turn off caching and that can be used within Javascript. You just need to install it by running npm install --save nocache
and then, add the below lines to your code.
const nocache = require('nocache')
app.use(nocache())
This is a solution that I found from this case here, where it's a solution to stop caching on client-side.
Upvotes: 0