2

I'm trying to set up caching headers on specific static file type in Spring Boot. In directory src/main/resources/static there are few subdirectories with different file types:

src/main/resources/static/font   --> *.otf
src/main/resources/static/lib    --> *.js
src/main/resources/static/images --> *.png, *.jpg

Is there a way to put cache headers by file type inside Spring configuration?

*.otf 365 days
*.png 30 days
*.jpg 7 days

Spring version is 5.2.3 and Spring Boot 2.2.4 - is there a chance that Spring Boot deals with it and makes it not work?

Tried with

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    final CacheControl oneYearPublic =
        CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic();
    // it does not "work" with "/static/fonts/"
    registry
        .addResourceHandler("/fonts/{filename:\\w+\\.otf}")
        .setCacheControl(oneYearPublic);
}

but I get weird results. When checking with Network tab of DevTools I get these headers:

    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Pragma: no-cache
    Expires: 0

But when I go to the URL directly, I get 404

http://localhost/fonts/1952RHEINMETALL.otf

Without any configuration I get "no-store" Cache-Control header.

horvoje
  • 643
  • 6
  • 21

1 Answers1

4

I've managed to have a working solution for this problem. Check the GitHub repo https://github.com/alexsomai/cache-static-assets.

This is an example of config that should work:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        Objects.requireNonNull(registry);

        // could use either '/**/images/{filename:\w+\.png}' or '/**/images/*.png'
        registry.addResourceHandler("/**/images/{filename:\\w+\\.png}")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS));

        registry.addResourceHandler("/**/images/*.jpg")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(2, TimeUnit.DAYS));

        registry.addResourceHandler("/**/lib/*.js")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(3, TimeUnit.DAYS));
    }
}

You could easily adjust it for your needs, based on file type and cache duration.

As key takeaways, make sure to add the addResourceLocations function (without this one, you get 404). Plus, if you are using Spring Boot, you don't need the @EnableWebMvc, as it was initially posted in this example https://stackoverflow.com/a/33216168/6908551.

Alexandru Somai
  • 1,395
  • 1
  • 7
  • 16
  • 1
    Previously tried with resource handlers but got weird results. Now I updated original question to let you see those results. Any idea what's going on? – horvoje Feb 14 '20 at 14:29
  • The 404 tells me that you have a different kind of issue, when accessing `/fonts/1952RHEINMETALL.otf`. What if you try to access `/static/font/1952RHEINMETALL.otf`, or `/font/1952RHEINMETALL.otf` (without s)? Are you sure that you are mapping correctly the URLs to your static resources? – Alexandru Somai Feb 14 '20 at 14:37
  • 404 is returned only when resource handlers are added. If I remove resource handlers, everything is accessible. Tried now the one you proposed but still 404. – horvoje Feb 14 '20 at 14:49
  • The URL is https://vegsh.com - you can watch the traffic. Then it goes more weird because I have no resource handlers deployed but Cache Control says "private". – horvoje Feb 14 '20 at 14:51
  • And I am aware of non-SSL protocol on my local host and SSL protocol on my server and I'm taking it into account when watching network traffic - meaning I don't make mistake with protocol to ask for SSL version on localhost and vice versa. – horvoje Feb 14 '20 at 14:54
  • 1
    I've managed to create a working solution for this use-case :) Check the GitHub repo https://github.com/alexsomai/cache-static-assets (I'll also update my answer with the correct version). It seems that the `addResourceLocations` was missing from the initial example. – Alexandru Somai Feb 16 '20 at 16:52
  • @horvoje did you have time to test the last update of my suggested changes? I'm really curious if it works in your case. – Alexandru Somai Feb 18 '20 at 17:23
  • Yes, your project works when I import it into the same IDE and sets expire dates as expected. But applying those resource handlers to my project - it won't work. Which tells me I have some misconfiguration or some dependency issues. – horvoje Feb 19 '20 at 14:25
  • Oh, sorry to hear that the configuration doesn't work in your project. – Alexandru Somai Feb 19 '20 at 14:43
  • 1
    Now I can confirm it works when /**/ is added as a prefix to addResourceHandler() parameter. Thanks! – horvoje Feb 19 '20 at 15:50
  • 1
    Just for the record for all those who will read this in the future - beware of your own hacks that handle URL mappings: I wanted to have favicon mapped to the root of my project but to have files inside resource directories; to have that I had to create SimpleUrlMapping bean and ResourceHttpRequestHandler. Headers should be handled additionally inside ResourceHttpRequestHandler. – horvoje Feb 20 '20 at 10:02