Express server adds an etag header on served HTML
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
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
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
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.
@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.
@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 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.
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. 🧹