40

By default, my browser caches webpages of my ExpressJS app.

This is causing a problem to my login system (users not logged in can open old cached pages of logged in users).

How do I disable this caching?

EDIT:

My app.js (main file):

var express = require('express');
var http = require('http');
var path = require('path');

var store = require('./routes/store');
var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 3012);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser('your secret here'));
  app.use(express.session());
  app.use(app.router);
  app.use(require('stylus').middleware(__dirname + '/public'));
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', store.home);
app.post('/', store.home);



app.get('/addProblem', store.addProblem);
app.post('/addProblem', store.addProblem);

app.get('/problem', store.problem);
app.post('/problem', store.problem);

app.get('/problemList', store.problemList);
app.post('/problemList', store.problemList);

app.get('/main', store.main);
app.post('/main', store.main);

app.post('/login', store.login);
app.get('/login', store.login);

app.get('/createProblem', store.createProblem);
app.post('/createProblem', store.createProblem);

app.post('/register', store.register);
app.get('/register', store.register);

app.post('/evaluate', store.evaluate);
app.get('/evaluate', store.evaluate);

app.get('/logout', store.logout);
app.post('/logout', store.logout);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});
4
  • What do you mean cached pages? The served JS files and CSS are cached?
    – aiapatag
    Commented Mar 25, 2014 at 13:43
  • @aiapatag, yes the entire pages is. Say I log in, log out and hit back, I'm now shown the "logged in" page.
    – batman
    Commented Mar 25, 2014 at 14:02
  • could you post how you did your middlewares? express.use()
    – aiapatag
    Commented Mar 25, 2014 at 14:55
  • 1
    @batman Did you ever find a solution to the problem? I'm having the same problem in 2017 Commented Feb 19, 2017 at 9:01

4 Answers 4

45

nocache

Don't waste your time reinventing the wheel, use the nocache middleware instead. It has been here for 8 years (2023) and it is downloaded more than 1.5 million times per week. Having only 100 stars on github, this is actually one of those unsung heroes of the express ecosystem.

nocache module weekly downloads

Install it

npm install --save nocache

Add it to you app:

const nocache = require('nocache');

app.use(nocache());

When installed as a middleware it sets four headers, disabling a lot of browser caching. This is the complete list of the updated headers.


Beware of ETag

Even if you are using nocache, the ETag header isn't removed, because it works in a different way. It's generated at the end of the request and could be another source of unintended caching. In order to handle it you have two choices.

app.set

The first is disabling it using express builtin app.set('etag', false); method.

on-headers

The second is removing the header just before it is sent to the client using the on-headers module:

const onHeaders = require('on-headers');

// install it as a middleware
app.use((req, res, next) => {
    // listen for the headers event
    onHeaders(res, () => {
        this.removeHeader('ETag');
    });
});

As pointed out in the comments this is actually a "ten-liner" package, but do you really want to copy and paste the same block of code on every express project? Or worse, publish another similar package? I don't think so and I'm sure your colleagues think the same too ;-)

3
  • 15
    This is one of those "almost" oneline packages, package sets these headers: res.setHeader('Surrogate-Control', 'no-store'); res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0');
    – Tosh
    Commented May 25, 2020 at 18:26
  • 3
    Do NOT install this package to save 4 lines of code. Reducing dependencies should always be one of your goals. Every dependency you add is another thing that needs to be updated, another way to get your project hacked, another way to add even more dependencies if this package adds dependencies, etc... You should never add a dependency for something you can do in a few lines of code yourself. Doing it yourself is not reinventing the wheel and more than using a for loop is instead of some "loop" package.
    – gman
    Commented Dec 21, 2022 at 18:26
  • 2
    You are right @gman, but sometimes a developer do not really know what he is doing. Pasting four lines of code inside a project without a real knowledge of what they do is worse than installing a dependency called nocache. If you are an experienced developer is up to you to choose between a dependency and code - I included all the headers in my answer for this reason. FYI: maybe nocache is one of the few npm libraries without dependencies and its author is a security expert.
    – lifeisfoo
    Commented Dec 22, 2022 at 9:55
42

There are two things to consider when dealing with cache in Express.js - ETag and Cache-Control headers.

ETag (MDN reference)
If you have dynamic content which does not benefit from ETags, it's best to disable it because it incurs small overhead with each request.

app.set('etag', false)

Cache-Control (MDN reference)
To completely disable cache, use the following header:

app.use((req, res, next) => {
  res.set('Cache-Control', 'no-store')
  next()
})

This header does not affect express.static() middleware. It handles cache in its own way.

6
app.disable('view cache');

^^ Code for ExpressJS

1

You can create a middleware, set headers in it so that there is no caching, and use in those route handlers that require authorization.

middleware cookies:

const cookie = async (req, res, next) => {
  try {
    const token = req.cookies.token;

    const check = jwt.verify(token, process.env.JWT_SECRET);
    const user = await User.findOne({_id: check._id, 'tokens.token': token});

    if (!user) {
      throw new Error();
    }

    req.token = token;
    req.user = user;

    res.set({
      "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
      "Pragma": "no-cache",
      "Expires": "0",
      "Surrogate-Control": "no-store"
    });
    next();
  } catch (e) {
    res.set({
      "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
      "Pragma": "no-cache",
      "Expires": "0",
      "Surrogate-Control": "no-store"
    }).redirect(301, '/login');
  }

};

used:

router.get('/all/tasks', cookie, async (req, res) => {
  try {
    const task = await Task.find({owner: req.user._id});

    if (!task) {
      return res.status(404).send();
    }

    res.status(200).render('tasks', {
      title: 'Your task',
      task: task
    });
  } catch {
    res.status(500).send();
  }
});

Ps: I took the headers from the nocashe library https://www.npmjs.com/package/nocache

Not the answer you're looking for? Browse other questions tagged or ask your own question.