volto icon indicating copy to clipboard operation
volto copied to clipboard

Express server adds an etag header on served HTML

Open tiberiuichim opened this issue 7 months ago • 6 comments

Describe the bug

This is the default behavior of express. The problem is that it is generated based on the sha1 of HTML body, which is always different. The main cause is the serialized window.__data which, on every request, it is different because:

  • it's an object and the ordering of its keys is not consistent
  • it includes the router redux state, which generates a random unique key every time

To Reproduce

Curl any Volto generated HTML page, look for the etag header. Make another similar request. Notice that the two etag headers are different.

Expected behavior

The etag should be the same if there hasn't been changes to the page.

Screenshots

Image

Software (please complete the following information):

  • Volto Version [e.g. 17.xx]

Additional context

This makes it impossible to benefit from caching of Volto's generated HTML

tiberiuichim avatar May 21 '25 07:05 tiberiuichim

The solution is to disable the etag for now, with

express().set('etag', false)...

in server.jsx. See https://stackoverflow.com/questions/24542959/how-does-a-etag-work-in-expressjs

tiberiuichim avatar May 21 '25 07:05 tiberiuichim

The main reason for inconsistent object ordering is the use of JSON.stringify, which doesn’t guarantee ordered key-value pairs. Instead, use:

const stringify = require('json-stable-stringify'); stringify(object); This ensures the serialized output has keys in a consistent order.

Also, the unique key that’s generated on every request (e.g., in the Redux state) can be replaced with a fixed placeholder to maintain stability:

const stableState = { ...reduxState, router: { ...reduxState.router, uniqueId: 'fixed-placeholder', // Replace random uniqueId with a constant } }; const serialized = stringify(stableState);

Using app.set('etag', false) does solve the immediate issue, BUT:

The root cause—unstable serialization of dynamic HTML data—remains.

Without ETags, clients won’t get 304 Not Modified responses, and will always re-download the full HTML.

This reduces caching efficiency and increases bandwidth usage.

Karnsaty1 avatar May 23 '25 15:05 Karnsaty1

@tiberiuichim I am working on this. Instead of completely disabling estag, we can disable it only for HTML responses and keep caching enabled for other files. Although ,I am currently testing a solution that will fix this issue completely.

Abhishek-17h avatar May 23 '25 17:05 Abhishek-17h

@tiberiuichim I'd like to take this issue and work on a proper solution using json-stable-stringify and normalization of dynamic values in the Redux state. Let me know if I can proceed!

Karnsaty1 avatar May 24 '25 09:05 Karnsaty1

@Karnsaty1 Yes,you can work on this issue and I have already done work in #7109 but I was not able to fully solve the issue.I have already tried json-stable-stringify but if you want to give a shot ,you can. If you can provide complete solution ,it will be very good.

Abhishek-17h avatar May 24 '25 09:05 Abhishek-17h

Hi There! 👋

We haven't seen any activity on this issue in a while :sleeping:, and we want to make sure that it's still relevant.

If updating to the latest version of Volto doesn't help, please let us know by:

  • adding a comment about what needs to be done next 💬
  • updating its status and other labels 🏷️

Otherwise close this issue. 🧹

github-actions[bot] avatar Nov 21 '25 00:11 github-actions[bot]