HTTP caching can significantly improve the performance of a web application. HTTP caching is built around the Cache-Control
response header and subsequently conditional request headers such as Last-Modified
and
ETag
. Cache-Control
tells private (eg browser) and public (eg proxy) caches how to cache
and reuse responses. The ETag
header is used for a conditional request, which may result in a 304
(NOT_MODIFIED) response without a body if the contents have not changed. ETag
can be considered a more
advanced successor to the Last-Modified
header.
This section describes the HTTP caching-related options available in Spring WebFlux.
CacheControl
CacheControl
provides support for configuring options associated with the Cache-Control
header and is accepted as an argument in a number of places:
-
Controllers
-
Static resources
Although RFC 7234 describes all
possible directives for Cache-Control
response header type CacheControl
takes a use-case
driven approach that focuses on common scenarios:
// Caching for an hour - "Cache-Control: max-age=3600"
CacheControl ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS);
/// Preventing caching - "Cache-Control: no-store"
CacheControl ccNoStore = CacheControl.noStore();
// Caching for ten days in public and private caches
// public cache should not convert the response
// "Cache-Control: max-age=864000, public, no-transform"
CacheControl ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic();
// Caching for an hour - "Cache-Control: max-age=3600"
val ccCacheOneHour = CacheControl.maxAge(1, TimeUnit.HOURS)
/// Preventing caching - "Cache-Control: no-store"
val ccNoStore = CacheControl.noStore()
// Caching for ten days in public and private caches
// public cache should not convert the response
// "Cache-Control: max-age=864000, public, no-transform"
val ccCustom = CacheControl.maxAge(10, TimeUnit.DAYS).noTransform().cachePublic()
Controllers
Controllers can add explicit support for HTTP caching. We recommend doing this because the lastModified
or ETag
value for a resource must be calculated before it can be compared with conditional request
headers. The controller can add the ETag
header and Cache-Control
parameters to the ResponseEntity
,
as shown in the following example:
@GetMapping("/book/{id}")
public ResponseEntity<Book> showBook(@PathVariable Long id) {
Book book = findBook(id);
String version = book.getVersion();
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.eTag(version) // lastModified is also available
.body(book);
}
@GetMapping("/book/{id}")
fun showBook (@PathVariable id: Long): ResponseEntity<Book> {
val book = findBook(id)
val version = book.getVersion()
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(30, TimeUnit.DAYS))
.eTag(version) // lastModified is also available
.body(book)
}
In the previous example, a 304 response (NOT_MODIFIED) is sent with an empty body if a comparison with the
conditional request headers indicates that the contents have not changed. Otherwise, the ETag
and
Cache-Control
headers are added to the response.
You can also check for conditional request headers in the controller, as shown in the following example:
@RequestMapping
public String myHandleMethod(ServerWebExchange exchange, Model model) {
long eTag = ...
if (exchange.checkNotModified(eTag)) {
return null;
}
model.addAttribute(...);
return "myViewName";
}
- Application-specific calculation.
- The response was set to 304 (NOT_MODIFIED). No further processing occurs.
- Continue processing the request.
@RequestMapping
fun myHandleMethod (exchange: ServerWebExchange, model: Model): String? {
val eTag: Long = ...
if (exchange.checkNotModified(eTag)) {
return null
}
model.addAttribute(...)
return "myViewName"
}
- Application-specific calculation.
- The response was set to 304 (NOT_MODIFIED). No further processing occurs.
- Continue processing the request.
There are three options for checking conditional requests against eTag
values, lastModified
values, or both. For conditional GET
and HEAD
requests, you can set the response to 304
(NOT_MODIFIED). For conditional POST
, PUT
and DELETE
, you can instead set the
response to 412 (PRECONDITION_FAILED) to prevent simultaneous modification.
Static resources
For optimal performance, static resources should be handled using Cache-Control
and conditional response
headers. See the section on configuring static resources.