HTTP Response Header for static content

Hi all,

I have a working flow that serves a page of HTML in response to a GET request. The HTML references some images that I have in a 'images' directory in the location configured in settings.js (/home/username/.node-red/node-red-static/). This all works as expected.

Except....I've been trying to preload images as I have a background image that changes periodically and want it to transition smoothly rather than line-by-line, etc, as sometimes happens, and discover that the content (image) is not being cached when preloaded and an inspection of the headers shows the following (example related to one of the images, but actually they all show the same):

Cache-Control: public, max-age=0

I've found a few articles about setting response headers using code in a function node (eg Setting headers with "HTTP Response" node) but this would appear to be relevant only for content served by a node and not served from the static folders.

Is anyone able to help with setting the Cache-Control header for static content?

Thanks

Chris

Hi,
The max-age=N response directive indicates that the response remains fresh until N seconds after the response is generated.

So increment the max-age

Thanks for the reply GogoVega,

I understand the need to increment max-age but where do I find that screen in relation to content served from the node-red-static folder?

With:

httpStatic: [
    {
            path: '/opt/static/',
            root: '/private/',
            middleware: myCustomHttpMiddleware
    }
]

The httpStatic entries can have a middleware

you could try adding a custom middleware for your images

in settings.js

const cacheControlMiddleware = (req, res, next) => {
  const ext = path.extname(req.url);

  // Check if the request is for JPG or PNG images
  if (ext === '.jpg' || ext === '.png') {
    // Set Cache-Control header for images
    res.setHeader('Cache-Control', 'public, max-age=86400');
  }

  // Continue to the next middleware
  next();
};


...

httpStatic: [
  { ..., middleware: [cacheControlMiddleware] }
]


:point_up: 100% untested and probably needs work - consider it an illustration only :point_up:

Thanks again...I assume that code gets added to settings.js?

And then the screen you captured appears in the editor somewhere?

Apologies if I'm missing something obvious but I'm not really understanding. Will attempt to add it to settings.js and see whether next steps become obvious.

Thanks again

Thanks Steve-mcl....this looks like it ties in with GogoVega's suggestions - will do some experimenting as this is an area I've had no previous experience in or even tinkered with. Thanks again

The image is an example of the http node.
Here you have to do it in the settings file by combining Steve's message and mine

httpStatic: [
    {
            path: '/home/username/.node-red/node-red-static/',
            root: '/static/',
            middleware: function (req, res, next) {
                const ext = path.extname(req.url);

                // Check if the request is for JPG or PNG images
                if (ext === '.jpg' || ext === '.png') {
                    // Set Cache-Control header for images
                    res.setHeader('Cache-Control', 'public, max-age=86400');
                }

                // Continue to the next middleware
                next();
            }
    }
]

Like Steve said, it's not sure to work

Do you also need to require path in order to use it ?

const ext = require('node:path').extname(req.url);

I don't know :man_shrugging:

You can use too:

if (req.url.endsWith('.jpg') || req.url.endsWith('.png')) { ... }
1 Like

Thanks again everyone, for your time and effort, I clearly have some reading to do - all I appear to be able to do so far is stop Node-RED from starting at all.

I've reverted everything so I have a working setup but clearly need to do some reading so will do that and continue to experiment.

Updates if I make progress....>

I believe that the correct method is to use req.get('Content-Type') which should return the mime type of the requested resource. This should be, at least in part, independent of the file name and particularly the file extension. It also doesn't need to require the path library.

OK, I think the above is out of date (though probably still works). You should use:

req.is(....)

Or you can use req.headers['content-type']

So, thanks again...I've made SOME progress:

with the following in settings.js I get the required Cache-Control header BUT I get it for everything (which is actually fine for my purposes but now that I've seen what you were all trying to do above I want to crack it for the images alone and it seems to be the condition that is breaking it)

httpStatic: [
  {
    path: '/home/username/.node-red/node-red-static/',
    root: '/static/',
    middleware: function (req, res, next) {
      // Set Cache-Control header for images
      res.setHeader('Cache-Control', 'public, max-age=86400');

      // Continue to the next middleware
      next();
    }
  }
],

Unfortunately, when I add in the condition using GogoVega's "endsWith" or "path.extname" examples Node-RED will not restart. I am still working on understanding enough about TotallyInformation's suggestion to write something based on that but am working on it.

Well I don't know what I was doing above because the following is now working but wasn't before, and I can't reproduce the issue - must have been a stray comma or similar

      httpStatic: [
        {
          path: '/home/username/.node-red/node-red-static/',
          root: '/static/',
          middleware: function (req, res, next) {
            if (req.url.endsWith('.jpg') || req.url.endsWith('.png')) {
              // Set Cache-Control header for images
              res.setHeader('Cache-Control', 'public, max-age=86400');
            }
            // Continue to the next middleware
            next();
          }
        }
      ],

and is caching only JPGs and PNGs from the static folder.

Thanks again all, very much, for your input - working solution and learned something new too!

(Still haven't understood enough about req.is() to get that working, sorry TotallyInformation - maybe I'll try again later)

In fact what Julian says is to search for the content type from the header instead of using the file name.

Look at What are all the possible values for HTTP "Content-Type" header? - Stack Overflow

Thanks, yes I understood that from the information in the page he linked. I think the reason it isn't working for me is that I don't know what the 'type' should be - for html it would clearly be 'text/html' but for a JPG I've tried 'image/jpg' and 'image' but neither work.

What should the content type be in the request header for my JPGs? Or, how can I find that? I found the Cache-Control info by inspecting the response headers in Google Chrome's "inspect" feature but am not seeing anything for content-type in the request headers in the same tool.

Strange, jpeg exists but not jpg :sweat_smile:

Maybe linked

Ah!

Does that mean it won't match or that I should use jpeg to match my JPGs? I'll try...

Sorry, just seen your updated message. I will try and see if they are linked and that image/jpeg works with my .jpg files

Hmmmm, well I can't get that working with 'image/jpeg' either. It does work with the

req.url.endsWith('.jpg')

however, so I will keep it configured that way and maybe return to this as a learning opportunity later.

Thanks, all, for your input and time.

1 Like