Spring Boot
Cache-Control
static resources
caching strategies
web development

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

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Adding a Cache-Control header to static resources is one of the simplest ways to make a Spring Boot application feel faster. The main decision is not whether to add caching, but how aggressive the cache should be for files such as JavaScript, CSS, fonts, and images that may change over time.

Why Static Resource Caching Matters

Browsers request static files repeatedly unless the response headers tell them otherwise. If the server marks those files as cacheable, the browser can reuse them instead of downloading them on every page load.

A typical policy for versioned static assets is:

  • 'public so shared caches may store the file.'
  • 'max-age set to a long duration.'
  • Fingerprinted filenames, such as app.9f1c2d.js, so a content change results in a new URL.

If filenames are not versioned, use shorter cache lifetimes to avoid serving stale files after a deployment.

Configure Cache Headers in Java

A version-agnostic Spring approach is to configure a resource handler with CacheControl:

java
1import java.util.concurrent.TimeUnit;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.http.CacheControl;
4import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
5import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6
7@Configuration
8public class StaticResourceConfig implements WebMvcConfigurer {
9
10    @Override
11    public void addResourceHandlers(ResourceHandlerRegistry registry) {
12        registry.addResourceHandler("/static/**")
13                .addResourceLocations("classpath:/static/")
14                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic());
15    }
16}

With this configuration, files under src/main/resources/static that are served through /static/** receive a Cache-Control header with a long max age.

This approach is explicit and easy to review because the caching policy lives in application code.

Match the Policy to the Asset Strategy

Long-lived caching is safe only when the URL changes whenever the content changes. If you serve /static/app.js and replace the file during deployment, browsers may keep the old file until the cache entry expires.

That is why many production setups combine long max-age values with content hashing in filenames. Without that, you may prefer a shorter setting such as a few minutes or hours.

A more conservative example is:

java
.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic())

That still reduces repeated downloads while lowering the risk of stale assets.

Spring Boot Property-Based Configuration

Depending on the Spring Boot version, some static resource cache settings can also be expressed through configuration properties. The exact property names have changed across Spring Boot releases, so Java configuration is often the safest answer when you want a stable pattern that works across versions with minimal ambiguity.

If you do use properties, verify them against the Spring Boot version running in your application rather than copying a snippet blindly from an older example.

Validate the Header

After configuring the handler, confirm the response header with a simple HTTP request:

bash
curl -I http://localhost:8080/static/app.js

You should see a response header similar to:

text
Cache-Control: max-age=31536000, public

If the header is missing, check whether the request path actually matches the configured resource handler pattern.

When Not to Cache Aggressively

Do not apply long-lived cache headers to user-specific or frequently changing data just because it is served over HTTP. Static resources and dynamic responses are different categories.

For example, generated HTML or API responses often need very different caching rules. Cache static assets aggressively only when they are genuinely safe to reuse.

Common Pitfalls

A common mistake is using a very long max-age for non-versioned filenames. That often leads to browsers serving old CSS or JavaScript after a release.

Another issue is configuring the wrong path pattern. If the handler is registered for /static/** but the app serves assets from a different URL path, the header will never be applied.

People also assume Spring Boot properties are identical across versions. Some examples online use older property names, so confirm them before relying on them.

Finally, do not forget to test with a real HTTP request. It is easy to add configuration and assume the header is present without verifying the actual response.

Summary

  • Add Cache-Control headers to static resources to reduce repeated downloads.
  • 'WebMvcConfigurer with CacheControl is a clear, version-stable Spring approach.'
  • Use long cache lifetimes only when asset URLs change with content changes.
  • Validate the response with curl -I or browser developer tools.
  • Keep dynamic responses and static asset caching policies separate.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.