In an annotation-based Spring MVC controller, what is the preferred way to set cache headers for a specific path?
-
For new users, [this is probably the answer you want](https://stackoverflow.com/a/40172925/476716) – OrangeDog Jul 16 '19 at 14:56
10 Answers
I just encountered the same problem, and found a good solution already provided by the framework. The org.springframework.web.servlet.mvc.WebContentInterceptor
class allows you to define default caching behaviour, plus path-specific overrides (with the same path-matcher behaviour used elsewhere). The steps for me were:
- Ensure my instance of
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
does not have the "cacheSeconds" property set. Add an instance of
WebContentInterceptor
:<mvc:interceptors> ... <bean class="org.springframework.web.servlet.mvc.WebContentInterceptor" p:cacheSeconds="0" p:alwaysUseFullPath="true" > <property name="cacheMappings"> <props> <!-- cache for one month --> <prop key="/cache/me/**">2592000</prop> <!-- don't set cache headers --> <prop key="/cache/agnostic/**">-1</prop> </props> </property> </bean> ... </mvc:interceptors>
After these changes, responses under /foo included headers to discourage caching, responses under /cache/me included headers to encourage caching, and responses under /cache/agnostic included no cache-related headers.
If using a pure Java configuration:
@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
/* Time, in seconds, to have the browser cache static resources (one week). */
private static final int BROWSER_CACHE_CONTROL = 604800;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/images/**")
.addResourceLocations("/images/")
.setCachePeriod(BROWSER_CACHE_CONTROL);
}
}
See also: http://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html

- 30,436
- 41
- 178
- 315

- 1,939
- 1
- 14
- 16
-
1I tried to use this for images stored in the DB. I can easily retrieve them. They show up, but FireBug always keeps telling me status "200 Ok", so no caching. Any ideas? – Czar Jan 31 '11 at 12:56
-
1This won't work because I don't have a prefix of mvc, or p. What are they supposed to be? – Jesse Jashinsky Nov 16 '12 at 17:48
-
Jesse - check the root element of your context def file; you should have a namespace element like 'xmlns:mvc="http://www.springframework.org/schema/mvc"', and another attribute named 'xsi:schemaLocation' with a value that includes 'http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd'. See http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html#format-configuring-FormattingConversionService for an example of a typical XML config. – Eric R. Rath Nov 26 '12 at 06:23
-
-
1You should only need to add `xmlns:p="http://www.springframework.org/schema/p"` as long as you're using the 3.1 xsd. – Eric R. Rath Jan 21 '14 at 16:58
-
This setup is not working for me. I did not do the
thing I explicity mentioned a – user590849 Jan 29 '16 at 18:302345
The answer is quite simple:
@Controller
public class EmployeeController {
@RequestMapping(value = "/find/employer/{employerId}", method = RequestMethod.GET)
public List getEmployees(@PathVariable("employerId") Long employerId, final HttpServletResponse response) {
response.setHeader("Cache-Control", "no-cache");
return employeeService.findEmployeesForEmployer(employerId);
}
}
Code above shows exactly what you want to achive. You have to do two things. Add "final HttpServletResponse response" as your parameter. And then set header Cache-Control to no-cache. -
10this would have to be added to every request. not a good solution for adding the header to all requests across the board. – yincrash Jul 28 '10 at 19:15
-
-
@yincrash, quoting OP: _what is the preferred way to set cache headers for a **specific path**_ – Klesun Jul 28 '20 at 13:53
org.springframework.web.servlet.support.WebContentGenerator, which is the base class for all Spring controllers has quite a few methods dealing with cache headers:
/* Set whether to use the HTTP 1.1 cache-control header. Default is "true".
* <p>Note: Cache headers will only get applied if caching is enabled
* (or explicitly prevented) for the current request. */
public final void setUseCacheControlHeader();
/* Return whether the HTTP 1.1 cache-control header is used. */
public final boolean isUseCacheControlHeader();
/* Set whether to use the HTTP 1.1 cache-control header value "no-store"
* when preventing caching. Default is "true". */
public final void setUseCacheControlNoStore(boolean useCacheControlNoStore);
/* Cache content for the given number of seconds. Default is -1,
* indicating no generation of cache-related headers.
* Only if this is set to 0 (no cache) or a positive value (cache for
* this many seconds) will this class generate cache headers.
* The headers can be overwritten by subclasses, before content is generated. */
public final void setCacheSeconds(int seconds);
They can either be invoked within your controller prior to content generation or specified as bean properties in Spring context.

- 99,456
- 24
- 206
- 195
-
3But, as I wrote in my question, I use an annotation-based controller that doesn't subclass from any spring base class. How will this help me? – D. Wroblewski Sep 01 '09 at 20:07
-
4If you want to alter cache settings depending on **specific path**, extending `AbstractController` is by far the easiest solution. If you want to apply your cache settings to all controllers, you can specify them on `AnnotationMethodHandlerAdapter` instance in Spring context for annotation-based controllers. Here's an example: http://static.springsource.org/spring/docs/2.5.6/reference/mvc.html#mvc-ann-initbinder (disregard the init binder, you don't need it) – ChssPly76 Sep 01 '09 at 21:06
-
6Because my question concerned how to do this with an annotations-based controller. Your solution only applies to controllers that subclass WebContentGenerator. Isn't it so? – D. Wroblewski Sep 07 '09 at 10:13
-
I've adressed that in my above comment. If you think you'll find a better solution I wish you luck. – ChssPly76 Sep 07 '09 at 17:33
-
1that's why I up-voted your comment. Why didn't you edit your answer instead? – D. Wroblewski Sep 07 '09 at 19:49
-
To clarify, AnnotationMethodHandlerAdapter does extend WebContentGenerator which gives you all the cache header options. Remember, you need to set
or a positive value for any of the cache headers to show up. – yincrash Jul 28 '10 at 20:23
Starting with Spring 4.2 you can do this:
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class CachingController {
@RequestMapping(method = RequestMethod.GET, path = "/cachedapi")
public ResponseEntity<MyDto> getPermissions() {
MyDto body = new MyDto();
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(20, TimeUnit.SECONDS))
.body(body);
}
}
CacheControl
object is a builder with many configuration options, see JavaDoc

- 22,140
- 8
- 55
- 44
You could use a Handler Interceptor and use the postHandle method provided by it:
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
then just add a header as follows in the method:
response.setHeader("Cache-Control", "no-cache");

- 62,090
- 32
- 125
- 150
I found WebContentInterceptor
to be the easiest way to go.
@Override
public void addInterceptors(InterceptorRegistry registry)
{
WebContentInterceptor interceptor = new WebContentInterceptor();
interceptor.addCacheMapping(CacheControl.noCache(), "/users", "admin");
registry.addInterceptor(interceptor);
}

- 2,055
- 19
- 20
you can define a anotation for this: @CacheControl(isPublic = true, maxAge = 300, sMaxAge = 300)
, then render this anotation to HTTP Header with Spring MVC interceptor. or do it dynamic:
int age = calculateLeftTiming();
String cacheControlValue = CacheControlHeader.newBuilder()
.setCacheType(CacheType.PUBLIC)
.setMaxAge(age)
.setsMaxAge(age).build().stringValue();
if (StringUtils.isNotBlank(cacheControlValue)) {
response.addHeader("Cache-Control", cacheControlValue);
}
Implication can be found here: 优雅的Builder模式
BTW: I just found that Spring MVC has build-in support for cache control: Google WebContentInterceptor or CacheControlHandlerInterceptor or CacheControl, you will find it.

- 1,294
- 15
- 20
I know this is a really old one, but those who are googling, this might help:
@Override
protected void addInterceptors(InterceptorRegistry registry) {
WebContentInterceptor interceptor = new WebContentInterceptor();
Properties mappings = new Properties();
mappings.put("/", "2592000");
mappings.put("/admin", "-1");
interceptor.setCacheMappings(mappings);
registry.addInterceptor(interceptor);
}

- 281
- 1
- 5
- 12
In your controller, you can set response headers directly.
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);

- 2,351
- 4
- 31
- 50
You could extend AnnotationMethodHandlerAdapter to look for a custom cache control annotation and set the http headers accordingly.