Old theme resources are loaded even after restart due to theme caching

Hi all,

I have developed a custom theme which works fine. It is deployed as an archive as described in the docs.

However when I deploy a new version, the old resources (JS files in a resources/react folder) are still being loaded. Even after a server restart (docker container restart), it still loads the old resources.

I am aware of disabling theme caching and this is the only option for the moment. By doing so, the new resources are loaded. But I don’t like this in a production environment.

The weird thing is: if I re-enable theme caching, the old resources are loaded again.

Can anyone help?

Running Keycloak 12.0.2

1 Like

I have noticed the same odd behavior in 11.0.3 also, but I didn’t notice it before 11.

The weird thing is: if I re-enable theme caching, the old resources are loaded again.

I also see this and can’t figure it out. The old content is no longer available in the theme, so it must be browser based. Perhaps this is related to the cache-control or etags headers on theme content. Does anyone have experience on how these headers are used in keycloak?

Even when disabling cache ( Cache-Control: no-cache and Pragma: no-cache in request header) the old version is returned.

Something extremely odd I just found out: when requesting the same resource from the same machine but with curl instead of the browser, the new version is returned.

When looking at the traffic on the server, I see both type of requests coming in.

@jens1 Can you post a concrete example? full URL you’re requesting, with all request headers and the response given? as well as the curl command you’re using?

I think the curl example you give means it’s definitely a browser behavior that’s causing the cached resource to be returned. If you clear the browser cache, you will probably see it fetch the new resource.

I’m not an HTTP expert, but correct use of ETags might eliminate this problem. However, I don’t know if there has been any effort in Keycloak to use those.

@xgp when I fetch with curl without parameters, I get the new version:
curl http://myhost.example.org:8080/auth/resources/3jqgc/login/mytheme/css/style.css

In Chrome dev tools, network tab, you can right click the call that fetches the (old) resource and select “Copy as cURL”, which includes all headers as it is executed by the browser. If I execute that curl command, I get the new version. I just ommited the --compressed option because that one wasn’t supported in the installed libcurl lib.

I don’t think the browser uses a cached version of the resource: in that case I wouldn’t see the call appear in the traffic logs of my server, right?

I believe that GzipResourceEncodingProviderFactory::initCacheDir should be called on each deploy.

DefaultThemeManagerFactory::themeCache gets cleared on each deploy, but the old files from GzipResourceEncodingProviderFactory::cacheDir get loaded, since this directory isn’t re-initialized.

This does solve the mistery why it was working with curl. Curl doesn’t pass the Accept-Encoding header, but the browser does (by default) (apperently this header wasn’t copied when “Copy as curl” in Chrome). When this header’s value contains “gzip”, the GzipResourceEncodingProvider will be used, which doesn’t reload the gzip directory on deploy. If this header isn’t present, like in curl, ClassLoaderTheme::getResourceAsStream is called which returns the new file.

Bug reported: https://issues.redhat.com/browse/KEYCLOAK-17767

Great sleuthing. Thanks for filing the bug.

1 Like

I think I’m being bitten by this. Is it possible to forcibly clear the theme caches completely?