简体   繁体   中英

How to add Cache-Control header to static resource in Spring Boot?

How can I add Cache-Control HTTP header in Spring Boot for static resources?

Tried using a filter-component in the application, which writes headers correctly, but Cache-Control header gets overwritten.

@Component
public class CacheBustingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
                                              throws IOException, ServletException {

        HttpServletResponse httpResp = (HttpServletResponse) resp;
        httpResp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        httpResp.setHeader("This-Header-Is-Set", "no-cache, no-store, must-revalidate");
        httpResp.setHeader("Expires", "0");

        chain.doFilter(req, resp);
    }

What I get in the browser is:

Cache-Control:no-store
This-Header-Is-Set:no-cache, no-store, must-revalidate
Expires:0

What I would like is:

Cache-Control:no-cache, no-store, must-revalidate
This-Header-Is-Set:no-cache, no-store, must-revalidate
Expires:0

This happens because of Spring Security: it rewrites all cache headers to disable caching totally. So we need to do two things:

  1. Disable spring security for static resources
  2. Enable static resource cache processing

In current version of Spring Boot we can change this behavior in application.properties config.

Disable spring security for some resources:

# Comma-separated list of paths to exclude from the default secured 
security.ignored=/myAssets/**

Enable sending cache headers for static resources:

# Enable HTML5 application cache manifest rewriting.
spring.resources.chain.html-application-cache=true

# Enable the Spring Resource Handling chain. Disabled by default unless at least one strategy has been enabled.
spring.resources.chain.enabled=true
# Enable the content Version Strategy.
spring.resources.chain.strategy.content.enabled=true 
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.content.paths=/** 

# Locations of static resources.
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/

That's all. Now Spring will check if your static files was changed and can send smarter responses (If-Modiffied-Since and others) and rewrite your appcache also.

Also, if there are reasons to not use content-based version for some resources - you can use alternate FixedVersion strategy and set version explicitly in your config:

#Enable the fixed Version Strategy.
spring.resources.chain.strategy.fixed.enabled=false 
# Comma-separated list of patterns to apply to the Version Strategy.
spring.resources.chain.strategy.fixed.paths= 
# Version string to use for the Version Strategy.
spring.resources.chain.strategy.fixed.version= 

See more in docs

As per the documentation , of ResourceHandlerRegistry . It is pretty easy. (I have no code related to it right now.)

In the place where you configure your static resources just add addResourceHandler method, It will return ResourceHandlerRegistration object.

There you can use setCacheControl method. What you have to do is configure and set a CacheControl obejct.

This is since spring 4.2 , else you will have to do it like below.

@Configuration
@EnableWebMvc
@ComponentScan("my.packages.here")
public class WebConfig extends WebMvcConfigurerAdapter {


   @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").setCachePeriod(0);
    }

}

The are properties that control the default cache headers for resources:

spring.resources.cache.cachecontrol.max-age: 3600

https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

There are a lot of ways in spring boot for caching http resource. Using spring boot 2.1.1 and additionally spring security 5.1.1.

1. For resources using resourcehandler in code (UNTESTED):

You can add customized extensions of resources this way.

registry.addResourceHandler

Is for adding the uri path where to get the resource

.addResourceLocations

Is for setting the location in the filesystem where the resources are located( given is a relative with classpath but absolute path with file::// is also possible.)

.setCacheControl

Is for setting the cache headers (self explanatory.)

Resourcechain and resolver are optional (in this case exactly as the default values.)

@Configuration
public class CustomWebMVCConfig implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.noStore()
                    .mustRevalidate())
            .setCacheControl(CacheControl.noCache())
            .resourceChain(true)
            .addResolver(new PathResourceResolver());
    }
}

2. For resources using application properties config file

Same as above, minus the specific patterns, but now as config. This configuration is applied to all resources in the static-locations listed.

spring.resources.cache.cachecontrol.no-store=true
spring.resources.cache.cachecontrol.must-revalidate=true
spring.resources.cache.cachecontrol.no-cache=true

3. At controller level

Response here is the HttpServletResponse injected in the controller method as parameter.

response.setHeader(HttpHeaders.CACHE_CONTROL,
            "no-cache, must-revalidate, no-store");
response.setHeader("Expires", "0");

Maleenc's, answer is correct. However, there is one issue with this implementation.

The following code will provide the correct cache-control header on the first request, but not a future request that returns 304 (Not-Modified) will return the default cache-control header set by spring security. {code}

public void addResourceHandlers(ResourceHandlerRegistry registry) {         
    registry.addResourceHandler("/resources/**").setCacheControl(CacheControl.maxAge(10, TimeUnit.SECONDS));
}

I have raised up this issue to the spring team, see https://jira.spring.io/browse/SPR-15133 . Here was there response: "Now you shouldn't disable security cache-control headers for your whole application indeed; the proper way to disable those for a specific path (resource handling, here) is explained in that issue comment, see the "Workaround" section."

Using spring boot 1.3.3, I had a 404 answer using maleenc answer. I could correct it by adding a resource location:

@Configuration
public class HttpClientConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS))
                .addResourceLocations("/");
    }
}

We can also add Cache-Control header in interceptors:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    WebContentInterceptor interceptor = new WebContentInterceptor();
    interceptor.addCacheMapping(CacheControl.maxAge(60, TimeUnit.SECONDS)
      .noTransform()
      .mustRevalidate(), "/static/**");
    registry.addInterceptor(interceptor);
}

Then we don't have to specify resource locations.

https://www.baeldung.com/spring-mvc-cache-headers#cache-interceptors

I want to add some useful comments to the given answers which use @Override public void addResourceHandlers(ResourceHandlerRegistry registry) {} because I had some problems with it. They may be useful for others too.

Assuming the following directory structure of a default Spring Boot 2.4 application with Spring Web MVC.

src/main/resources/
|- static/
  |- res/
    |- css/
    |- js/
  |- images/
  |- favicon.ico

And we want to add caching for css, js, images and favicon. Then a Configuration would look like this:

import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.concurrent.TimeUnit;

@Configuration
public class CacheStaticResourcesConfiguration implements WebMvcConfigurer {

    /**
     * We provide a custom configuration which resolves URL-Requests to static files in the
     * classpath (src/main/resources directory).
     *
     * This overloads a default configuration retrieved at least partly from
     * {@link WebProperties.Resources#getStaticLocations()}.
     * @param registry ResourceHandlerRegistry
     */
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        /*
         * BE AWARE HERE:
         *
         * .addResourceHandler(): URL Paths
         * .addResourceLocations(): Paths in Classpath to look for file
         *   root "/" refers to src/main/resources
         *   For configuration example, see:
         *     org.springframework.boot.autoconfigure.web.WebProperties.Resources().getStaticLocations()
         *
         * .addResourceLocations("classpath:/static/")
         *   =>
         *      addResourceHandler("/**")
         *      => GET /res/css/main.css
         *         => resolved as: "classpath:/static/res/css/main.css"
         *      BUT
         *      addResourceHandler("/res/**")
         *      => GET /res/css/main.css
         *            (spring only appends the ** to the value from
         *             addResourceLocations())
         *         => resolved as: "classpath:/static/css/main.css"
         */

        registry
                .addResourceHandler("/favicon.ico")
                .addResourceLocations("classpath:/static/")
                .setCacheControl(CacheControl.maxAge(1, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());

        registry
                .addResourceHandler("/res/**")
                // trailing slash is important!
                .addResourceLocations("classpath:/static/res/")
                .setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());

        registry
                .addResourceHandler("/images/**")
                // trailing slash is important!
                .addResourceLocations("classpath:/static/images/")
                .setCacheControl(CacheControl.maxAge(7, TimeUnit.DAYS)
                        .noTransform()
                        .mustRevalidate());
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM