LiteSpeed Cache prevents browsers to re-validate pages

kvv213

New Member
#1
Hi EveryOne!

I'm using OLS 1.7.16 with WordPress 6.1.1 and LiteSpeed Cache for WP. The problem is that whe I use LSCWP then pages are not re-validated by the browser and not updated.

I have to WP installations at my hosting. One is with SuperCache and it works perfectly with updating of the pages. And one with LSCWP and here I have a small problem.

As I can see using the developer tools in Chromium the problem is that a browser takes page from the blog that uses LSCWP until the max-age is ended or a user press F5.

I see the following in the headers:

Code:
    cache-control: public, max-age=604800
    x-litespeed-cache: hit
The page is in cache - that is very good and I use LSCWP exactly for this. But if I change a blog record then it is not updated in the browser (the cache at the server is re-generated). The browser takes the page from its local cache (disk, it is visible in the developers tools).

OLS doesn't include eTag for generated pages (but do this for all the static files, except cached dynamic files).

From the other side SuperCache works well. It provides the following header in the most cases cache-control: max-age=3, must-revalidate and the browser check the page before taking it from the local cache.

Blog page with LSCWP https://octoglass.ru/news/en/620-2/ and with SuperCache https://blog.kvv213.com/2022/12/syn...nogo-obnaruzheniya-i-uskorenie-sinhronizacii/

The server is the same but the virtual hosts are different. Browser cache in LSCWP is disabled:
1674408182295.png

What can be wrong? I'd like to make the dynamic cached pages to be re-validated before taken from the browser cache.
 

Cold-Egg

Administrator
#2
The browser cache is actually enabled on the server level due to OLS only recognizes rewrite rules, and it will only apply to the static files only. The "cache-control: max-age=3 " is not efficient, it's similar to no browser cache.
I'm not clear about updating the blog issue, please describe it more. Does the new content not updating in public or?
 

kvv213

New Member
#3
The browser cache is actually enabled on the server level due to OLS only recognizes rewrite rules, and it will only apply to the static files only. The "cache-control: max-age=3 " is not efficient, it's similar to no browser cache.
I'm not clear about updating the blog issue, please describe it more. Does the new content not updating in public or?
Exactly, the new content is placed but if I use a browser then (public, not private) I don't see the new content until I execute a refresh of a page. Browser always takes the page from its own cache and don't do validation of the page. That behaviour is valid for all Chromium browsers. Didn't check it with Mozilla's variants.

From my point of view the problem can be in two points:
1. Absence of etag for dynamic pages. In that case the browser is not able to compare the hash of the etags and decide to download the page again.
2. Absence of necessary headers according to cache settings. `max-age=3` is too low for a normal cache value - fully agree. But also `must-revalidate` can be a remedy. Also `no-cache` (or `max-age=0` can be an answer. Accroding to the specification `no-cache` doesn't prevent a browser to store the page in its cache. It requires re-validate the page during the next request of the page from the server (`no-store` prevents the browser form storing a page in its cache).

In the private mode the plugin provides correct values `cache-control: no-cache, must-revalidate, max-age=0`

As I understand the headers can be replaced at OLS level with a Context for dynamic pages. I've tried with no success (I think that I've missed some parameters). So will retry.

PS. I've noticed that behaviour when was evaluating OLS so since then I have two blogs with different caching plugins. Have plans to move completely to LS solutions. But need to fix all the rough corners :)
 
Last edited:

Cold-Egg

Administrator
#4
1. The etag gone issue usually happens due to the remove etag rule placed in the .htaccess file.
2. There's no cache-control header on the dynamic page, so I'm not sure why the browser cache is related.
Code:
curl -I https://octoglass.ru/news/en/620-2/
HTTP/2 200
content-type: text/html; charset=UTF-8
x-pingback: https://octoglass.ru/news/xmlrpc.php
link: <https://octoglass.ru/news/wp-json/>; rel="https://api.w.org/"
link: <https://octoglass.ru/news/wp-json/wp/v2/posts/620>; rel="alternate"; type="application/json"
link: <https://octoglass.ru/news/?p=620>; rel=shortlink
vary: Accept-Encoding
server: LiteSpeed
strict-transport-security: max-age=63072000; includeSubDomains; preload
alt-svc: h3=":443"; ma=2592000, h3-29=":443"; ma=2592000, h3-Q050=":443"; ma=2592000, h3-Q046=":443"; ma=2592000, h3-Q043=":443"; ma=2592000, quic=":443"; ma=2592000; v="43,46"
x-litespeed-cache: hit
date: Mon, 23 Jan 2023 08:20:14 GMT
 

kvv213

New Member
#6
I've check this once more.

1. Regarding to eTag - there is no mentions of this in .htaccess I have for the site including WordPress site. Moreover if they were there they shouldn't be used by OLS because it works only with RewriteRule (according to the documentation). Anyway noticed some functions in WordPress that can generate eTags. But I don't see eTags for the dynamic pages not for LSCWP enabled site nor for SuperCache.
2. Now I don't see any cache-control at the dynamic pages o_O now. And they works well, if a page was changed at WP side the browser shows it.
3. I see only 200 responses. No 304. That is not good. as @LiteCache mentioned.

Actually I didn't do anything with the sites except two things:

a. Removed Expires Default at VirtualHost level and added other types:
1674752544614.png

b. Browser cache in LSCWP plugin:
1674752623475.png
It was turned off. But I turned it off a little bit more :)

I'd like to check Browser Cache option once more.
 

kvv213

New Member
#7
OK. Some more tests:

1. I've enabled Browser cache in LSCWP.
Nothing happened. I still receive 200 code, no `cache control` in headers. Pages are updated without their reload with F5. Purging all the cache didn't helped.
So, the behaviour is not changed.

2. Reboot OLS-server. Nothing changed. I still have only 200 code with no `cache control` in the headers at all.

3. Added Default Expires. Reboot OLS. The same behaviour. Everything works except 304 code.

4. Removed Expires By Type at OLS.
1674754473754.png
And here I got cache-control. But still no 304 code.

1674754448912.png

and here I have the problem behaviour when a page is updated at WP side, LSCWP cleared the cache but the browser takes the page from its own cache. Purging of the cache at LSCWP side doesn't help. Reload page helps to get the updated page.

So. There is something not correct:
a. `Browser Cache` options at LSCWP doesn't change the behaviour.
b. `Cache-control` appears only when all the types of content is marked with Expires at OLS-server.
c. Browser always receives only 200 code*.
d. Browser when receives `Cache-control` takes page from its own cache and don't do any type of validation.

What to do :) or To Do or Not To Do.

* I've checked the server logs. There are some 304 records of resources from the blog. Like CSS. I've reload pages a lot today and not 304 today.

PS. I also noticed that LSCWP shows the following:
  1. x-litespeed-cache:
    miss
  2. x-litespeed-cache-control:
    public,max-age=604800
  3. x-litespeed-tag:
    e96_HTTP.200,e96_home,e96_URL.eba58219bbe33ec4b191ce6f86e3aaa4,e96_F,e96_,e96_MIN.577b5f2cdab1f0997059508dc458230e.css,e96_MIN.ff214e66def6da8741d57ca9f5dec58f.js

Only when it misses the cache. If it hits no
  • x-litespeed-cache-control:
    public,max-age=604800

Appeared.
 
Last edited:

LiteCache

Active Member
#8
1. Regarding to eTag - there is no mentions of this in .htaccess I have for the site including WordPress site. Moreover if they were there they shouldn't be used by OLS because it works only with RewriteRule (according to the documentation). Anyway noticed some functions in WordPress that can generate eTags. But I don't see eTags for the dynamic pages not for LSCWP enabled site nor for SuperCache.
The normal case is that etag does not have to be set, as it is set by default. The normal case refers to LSWS and Apache. Unfortunately, I don't know how this works with OLS. In the event that WP would set etags, they would have no function on cached pages. That's why we forget this option very quickly.

2. Now I don't see any cache-control at the dynamic pages o_O now. And they works well, if a page was changed at WP side the browser shows it.
With Wordpress there is no cache-control header for dynamic pages, at least I am not aware of this when using the OLS.

3. I see only 200 responses. No 304. That is not good. as @LiteCache mentioned.
For a 304 status code, etags are needed, especially for dynamic pages. However, the fundamental question arises as to whether the OLS supports this. It's not applicable for OLS, but the status code 304 along with etags and the "if modified since" request header is only available when ESI is off. Since OLS doesn't support ESI, this is just a note.

a. Removed Expires Default at VirtualHost level and added other types:
You should set the times for the expire settings higher. Google expects 31536000 for this.

b. Browser cache in LSCWP plugin:
This setting cannot be used when using OLS and must be made via the OLS CP. However, you should expand the existing settings according to the following stipulations.

Code:
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/gif "access plus 31557600 seconds"
    ExpiresByType application/json "access 31557600 seconds"
    ExpiresByType image/jpeg "access plus 31557600 seconds"
    ExpiresByType image/jpg "access plus 31557600 seconds"
    ExpiresByType image/png "access plus 31557600 seconds"
    ExpiresByType image/webp "access plus 31557600 seconds"
    ExpiresByType image/avif "access plus 31557600 seconds"
    ExpiresByType text/css "access plus 31557600 seconds"
    ExpiresByType text/javascript "access plus 31557600 seconds"
    ExpiresByType application/x-javascript "access plus 31557600 seconds"
    ExpiresByType application/vnd.ms-fontobject "access plus 31557600 seconds"
    ExpiresByType application/x-font-ttf    "access plus 31557600 seconds"
    ExpiresByType application/x-font-woff   "access plus 31557600 seconds"
    ExpiresByType font/opentype             "access plus 31557600 seconds"
    ExpiresByType image/svg+xml             "access plus 31557600 seconds"
</IfModule>
1. I've enabled Browser cache in LSCWP.
Nothing happened. I still receive 200 code, no `cache control` in headers. Pages are updated without their reload with F5. Purging all the cache didn't helped.
see above

2. Reboot OLS-server. Nothing changed. I still have only 200 code with no `cache control` in the headers at all.
see above

4. Removed Expires By Type at OLS.
see above

3. Added Default Expires. Reboot OLS. The same behaviour. Everything works except 304 code.
see above



Change the Expire settings as shown above. With the Expires Default setting, you inevitably activate the browser cache for dynamic pages, so that requesting dynamic pages always shows stale content.

You can also leave the Expires default setting if you increase the value to 31536000, but then you need a cache control setting or header for the dynamic page:

Code:
<FilesMatch "(\.html|\.htm|\.php)$">
    Header set Cache-Control "max-age=0, private, must-revalidate"
</FilesMatch>
 

kvv213

New Member
#9
For a 304 status code, etags are needed, especially for dynamic pages. However, the fundamental question arises as to whether the OLS supports this. It's not applicable for OLS, but the status code 304 along with etags and the "if modified since" request header is only available when ESI is off. Since OLS doesn't support ESI, this is just a note.
Actually OLS gives 304 code for static content like css, images and similar.
You should set the times for the expire settings higher. Google expects 31536000 for this.
Currently I've installed the following expires:
Code:
image/*=A31536000, text/css=A31536000, application/x-javascript=A31536000, application/javascript=A31536000, font/*=A31536000, application/x-font-ttf=A31536000, text/javascript=A31536000, application/x-font-woff=A31536000
These expires are enabled at the server level so they work for all the sites I have.

A means the user access plus xxx seconds.

I didn't enable Default expires.

Note: text/html is not enabled.

<FilesMatch "(\.html|\.htm|\.php)$"> Header set Cache-Control "max-age=0, private, must-revalidate" </FilesMatch>
OLS doesn't have such a feature via .htaccess. But I think it can be enabled via context feature in OLS. But because Default expire is not filled - I don't have a problem at Public cache. If I log in to WP then LSCWP automatically sets private cache with revalidation. So this is not required.

Regarding to eTag. Looking to OLS manuals I found out this "Specifies whether to use a file’s inode, last-modified time, and size attributes to generate the ETag HTTP response header for static files". So that means (or at least we can make such a conclusion) that OLS gives ETag only for static files and doesn't for dynamic content (but LSCWP makes dynamic content static). OLS also doesn't have ESI. So the only way to get etag with OLS + LSCWP is at LSCWP side. I'm not sure that it is possible to insert a dynamic etag to header via OLS context feature.

Currently there shouldn't be more problems, because:
1. Dynamic pages are provided to public without cache-control headers. So the browser should request the page once more.
2. Dynamic pages are provided to logged in users are provided with short expire and revalidation instructions. So the browser should check the page.
3. Static files (pictures etc) are provided with etag and with cache-control. So no problem is here.
4. Static html files are provided without cache-control but with etag. Browser decides to store the page in its cache and re-validate it in a case when etag is changed.

I was also tried to use text/html=A31536000 in the expires. No visible result. Dynamic and static pages are returned without cache-control header.

But, I've found a way how to get 304 code. It appears on static page when I reload it with F5.

1674850914106.png

Thank you!
 

LiteCache

Active Member
#10
Regarding to eTag. Looking to OLS manuals I found out this "Specifies whether to use a file’s inode, last-modified time, and size attributes to generate the ETag HTTP response header for static files". So that means (or at least we can make such a conclusion) that OLS gives ETag only for static files and doesn't for dynamic content (but LSCWP makes dynamic content static). OLS also doesn't have ESI. So the only way to get etag with OLS + LSCWP is at LSCWP side. I'm not sure that it is possible to insert a dynamic etag to header via OLS context feature.
You seem to have not or not yet understood a few basic things. So here is an update for you.

Cache-Control and Expire Directive are basically the same thing or have the same effect. The difference comes from the history of the HTTP protocol. The Expire directive originated from HTTP/1.0, cache-control from HTTP/1.1. Another distinction arises from the fact that the Expire directive defines a specific date in the future. Cache-Control, on the other hand, behaves like a backwards timer. But as already mentioned, the function is the same, although it seems advantageous to use cache control because the date does not have to be checked for every request. In the end, it's a matter of belief as to what's better or worse.

You haven't understood the etags either. Etag determines a timestamp in combination with the status code 304. If the server sets an etag response header, the client knows that the requested source does not have to be downloaded, but should use the content from the browser cache. However, a request still occurs. The server and client only exchange information. But no download takes place. The client/browser uses the "If-Modified-Since" request header for this. Without exception, etag is only important for dynamic sources, but not for static sources, which is crucial.

You use the etag header for static sources. This forces the browser to make a request even though the requested static source has long been in the browser cache. This is unnecessary overhead and will make your site slower rather than faster. It is therefore essential to deactivate etag for static sources.

Your site now generates the 304 status code (but only with a soft refresh), but the client's "If-Modified-Since" request header is missing. Because of this, your settings don't work as you would expect with a 304 status code.

The bottom line of it all is that you have completely misconfigured the server. OLS does not provide the same functionality as the LSWS. That's why I advise you to disable everything related to etags and only use the Expire directive!
 
#11
The bottom line of it all is that you have completely misconfigured the server. OLS does not provide the same functionality as the LSWS. That's why I advise you to disable everything related to etags and only use the Expire directive!
I've just checked the static files (OLS supports etag only for static) and didn't change the behaviour it the response codes. Web-server returns the same 200 when I just load a cached static resource and returns 304 when I reload it with F5.


2023-01-28 14-55-20 Корпоративный сайт Octoglass (Инкогнито).png

In both cases I have a problem that the static file will not be reloaded with the browser in a case of its modification. That means that if I change somehow css file then this css file (even with etag or without it) will be taken from the browser cach. That is not good behaviour because sometimes I need to modify the files in such a way (I know that it is good to use versions of the files in order to prevent its overcaching but not always it is useful).

Honestly I were thinking that with etag the browser will revalidate the file with the server.

What I did wrong?

As a remedy for this I think that I can do:
1. Reduce max-age to a reasonable level, like a week. But this is not good from Google side of view.
2. Add must-revalidate into the headers somehow.

PS. Currently I've returned etag to the server until further understanding.
PSS. As a result I think that I'll write an article for my blog with explanations for followers.
 

LiteCache

Active Member
#14
The statsus code 304 for static sources depends on used browser. Chrome always displays cached CSS sources and creates a 304 status code for cached static sources regardless of which expire or cache-control settings you have made.. Firefox behavior is different. FF uses status code 200 if static source is cached by browser, but only if it is javascript. All other types of static sources are hidden.
 
#15
The statsus code 304 for static sources depends on used browser. Chrome always displays cached CSS sources and creates a 304 status code for cached static sources regardless of which expire or cache-control settings you have made.. Firefox behavior is different. FF uses status code 200 if static source is cached by browser, but only if it is javascript. All other types of static sources are hidden.
I've just checked with Edge, Yandex.Browser, Chrome (fresh installed). The behaviour is the same. All have the same base engine - Chromium. I receive only 200 code with normal requests with or without etag to static files like css outside of WP. If I do reload then I get 304 for such resources.

May be a do something wrong?
 
#17
May I summarise my understanding taken from this thread and surfing of the specifications? I may be wrong in some cases but this is formed after some experiments with OLS:

1. Expires defines how long the resource can be considered fresh. In OLS this value is transformed into cache-control: max-age.
2. If I'd like to be sure that a resource (like css or js or html) is updated at the client and not taken from its cache. Then I need to reduce max-age to very small value or even zero. In all other cases the client's browser will take the resource from its own cache.
3. When max-age is not expired any browser request to resources ends with 200 code.
4. When max-age is expired then browser requests the resource once more and if it is not changed (etag helps with that) then it receives 304. The same with soft reload via F5 (hard reload with shift-f5 requests all the resources).
5. For dynamic pages LSCWP takes Default expired setting from OLS and the page is not re-requested until this Default is expired. If it is empty then no cache-control is added for public cache. This setting potentially can be changed via adding a personal expire for text/html. When a user is logged into WP then cache-control appears from LSCWP with no-cache directive (it wold be better to see also private here, but with SSL it is not required).
 

LiteCache

Active Member
#18
1. Expires defines how long the resource can be considered fresh. In OLS this value is transformed into cache-control: max-age.
Expire directive defines how long a requested source can be cached by browser, but expire is expire and not cache-control. Basically both do the same, but whenever you check response headers there must be a expire header. I can't imagine why OLS should make a Cache-Control header out of an Expire header?! I think you're misinterpreting something!?

2. If I'd like to be sure that a resource (like css or js or html) is updated at the client and not taken from its cache. Then I need to reduce max-age to very small value or even zero. In all other cases the client's browser will take the resource from its own cache.
Theoretically yes, in practice this is nonsense. Unless you work in a stage environment. If you change CSS regularly, it is recommended to use GET parameters appended to the URL of the CSS. Either a timestamp is set as a parameter or some other hash that changes whenever the CSS file is changed. This GET parameter forces the browser to download the source, even with a soft refresh and regardless of the status code.

3. When max-age is not expired any browser request to resources ends with 200 code.
Yes and no, it depends on kind of source (static or dynamic) and the used browser. Chrome has a different behaviour as FF with cached static sources. That cannot be generalized. In the end it doesn't matter and has no consequences for static sources. Ultimately, what counts with static sources is whether the respective browser shows you in the developer console, whether the source comes from the browser cache or from the server.

4. When max-age is expired then browser requests the resource once more and if it is not changed (etag helps with that) then it receives 304. The same with soft reload via F5 (hard reload with shift-f5 requests all the resources).
Now you're making it really complicated. You're wrong about everything you've come up with.

5. For dynamic pages LSCWP takes Default expired setting from OLS and the page is not re-requested until this Default is expired. If it is empty then no cache-control is added for public cache. This setting potentially can be changed via adding a personal expire for text/html. When a user is logged into WP then cache-control appears from LSCWP with no-cache directive (it wold be better to see also private here, but with SSL it is not required).
Sorry, again you are completely wrong.
 
#19
Expire directive defines how long a requested source can be cached by browser, but expire is expire and not cache-control. Basically both do the same, but whenever you check response headers there must be a expire header. I can't imagine why OLS should make a Cache-Control header out of an Expire header?! I think you're misinterpreting something!?
I'm sure that OLS moves its Expire to cache-control:
1674920240508.png
These are headers from a static file (CSS) directly from OLS (not WP with the plugin). max-age=7200 is just a copy of the text/css expire setting in OLS server. expires setting tells the same.
Theoretically yes, in practice this is nonsense. Unless you work in a stage environment. If you change CSS regularly, it is recommended to use GET parameters appended to the URL of the CSS. Either a timestamp is set as a parameter or some other hash that changes whenever the CSS file is changed. This GET parameter forces the browser to download the source, even with a soft refresh and regardless of the status code.
I agree with you that the best option will be use some query tail for a resource or version of a resource. But in my real word with one static and two dynamic pages using GET-requests (or rename the file) and change them all over the sites is too overhead. It would be better just to reduce max-age (expire) to a reasonable small value.
Moreover some plugins, for example, Wordpress Popular Pages, require reduce the cache time (for the dynamic page) due to REST access keys update requirement. Also I've noticed that many pages large and not large have small max-age. I understand that this may reduce the maximum capacity of http-server. But OLS is damn fast and after small tuning (thank's to Erik) can hold much stronger traffic that I have on all of my sites all together. Yes, and that will show also more traffic from my server (but again I use the cheapest VPS plan available with 2 vCores, 1 Gb of RAM but it has unlimited traffic and it cost me around 3.2 USD per a month).

Yes and no, it depends on kind of source (static or dynamic) and the used browser. Chrome has a different behaviour as FF with cached static sources. That cannot be generalized. In the end it doesn't matter and has no consequences for static sources. Ultimately, what counts with static sources is whether the respective browser shows you in the developer console, whether the source comes from the browser cache or from the server.
I think that from the browser side there is no difference between the dynamic and the static content except JavaScript and HTML5 scripts. It always receives text/html and doesn't know how it was prepared at the server side: pushed from a disk-file or generated via a PHP-script. The rules of caching are the same. We can manage the cache via headers that are provided by http-server (or in html page via a meta tag). The initial problem was in too large max-age in my setup because of Default expire setting in OLS.

I've just installed FF and it shows the same behaviour (no reload, just open pages):
1674921733610.png

and with F5:
1674921838863.png

I need to re-check the rest.

PS. I've switched the main blog site to LSCWP and everything seems good so far :)
1674921915927.png
Lighthouse rating is updated. Performance was 99, best practises 83. And this is without any optimization.
 

Attachments

Top