expressjs / express

Fast, unopinionated, minimalist web framework for node.
https://expressjs.com
MIT License
65.43k stars 15.99k forks source link

default no-cache headers #3804

Open dlants opened 5 years ago

dlants commented 5 years ago

Hi there!

By default, express does not serve no-cache headers on routes, so for instance:

import * as express from 'express'

const app = express()
app.get('/', (_req, res) => {
  res.send('Hello')
})

app.listen(3000)

Results in the following:

curl -I localhost:3000
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 5
ETag: W/"5-9/+ei3uy4Jtwk1pdeF4MxdnQq/A"
Date: Thu, 15 Nov 2018 22:53:08 GMT
Connection: keep-alive

This is missing Cache-Control: no-cache, Pragma: no-cahe or Expires: 0 headers that would inform the browser that the result of the request should not be cached.

My suggestion is to serve these files with no-cache headers by default, like serve-static. This will reduce the chance that folks will forget to configure it, and end up potentially having clients hang on to stale code.

(this is an issue I've recently run into on my project -- it seems that sometimes browsers do cache such responses when no cache headers are provided)

Thanks for your hard work!

dougwilson commented 5 years ago

But we want web browsers to cache the content when possible by default. They should still be sending a conditional request due to the etag. What browser are you using and can you put together a reproduction case that demonstrates the browser not seeing the new content?

dlants commented 5 years ago

Ah, that clarifies things! I didn't know about the Etag directive, and missed it in the output above. Thanks!

I think that totally makes sense as the default setting, and hopefully this issue will help some others that are following the same red herring as me. It's likely that something about our CDN is interfering with proper handling of the Etag header... since we're seeing chrome (69, 70) on ChromeOs and Windows 10 have the stale code issue.

Malvoz commented 5 years ago

One thing to note is that no-cache does not mean "do not cache" - that would be no-store, rather it means "do not serve this content from the cache without first validating it's freshness on the server".

And because @dlants didn't tell the client to first validate with the server, browsers have to rely on their own freshness heuristics in which case the content may be cached for some time.

I found this article which goes to the extent of looking at the source code of some of the major browsers, and verifies that content without cache-control or Expires is cached for "10% of the time since the object was last modified (provided that a Last-Modified header was returned)".

So if you have users downloading a web page (which does not use no-cache or a low max-age) that is still cached for 10% of the time since it was last modified, but has been updated since, breakage should be expected. This of course, should only be the case for documents, sub resources should preferably use file name versioning and a long max-age / immutable directive.