Spring MVC
Spring Boot
Static Content
Web Development
Java

Refreshing static content with Spring MVC and Boot

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

During development, changing a CSS, JavaScript, or HTML file in a Spring Boot application requires a server restart to see the update. This slows down front-end development. Spring Boot provides several ways to serve fresh static content without restarts: Spring DevTools enables automatic reload, resource versioning with content hashing ensures browsers fetch updated files, and cache-control headers prevent stale content in production. This article covers each approach for both development-time live reload and production-ready cache-busting.

Default Static Content Locations

Spring Boot serves static files from these classpath directories (in priority order):

  1. classpath:/META-INF/resources/
  2. classpath:/resources/
  3. classpath:/static/
  4. classpath:/public/

Files can also be served from file: locations outside the classpath.

 
1src/main/resources/
2├── static/
3│   ├── css/
4│   │   └── style.css
5│   ├── js/
6│   │   └── app.js
7│   └── images/
8│       └── logo.png
9└── templates/
10    └── index.html

Development: Spring DevTools (Live Reload)

Spring DevTools automatically restarts the application when classpath files change, and includes a LiveReload server that refreshes the browser:

xml
1<dependency>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-devtools</artifactId>
4    <scope>runtime</scope>
5    <optional>true</optional>
6</dependency>
properties
1# application.properties
2spring.devtools.restart.enabled=true
3spring.devtools.livereload.enabled=true
4
5# Trigger restart only on specific file changes
6spring.devtools.restart.additional-paths=src/main/resources/static
7# Exclude paths from triggering restart (just live reload)
8spring.devtools.restart.exclude=static/**,public/**,templates/**

With restart.exclude=static/**, changing CSS/JS files triggers a LiveReload (browser refresh) without a full application restart. Install the LiveReload browser extension for automatic refresh.

IDE Configuration

For IntelliJ IDEA, enable automatic build on save:

  1. Settings > Build, Execution, Deployment > Compiler > "Build project automatically"
  2. Settings > Advanced Settings > "Allow auto-make to start even if developed application is currently running"

For Eclipse/STS, automatic build is enabled by default.

Development: Serve from File System

Serve static files directly from the file system so changes are visible immediately without any restart:

properties
# Serve static files from the project directory (not classpath)
spring.web.resources.static-locations=file:src/main/resources/static/,classpath:/static/
java
1@Configuration
2public class WebConfig implements WebMvcConfigurer {
3    @Override
4    public void addResourceHandlers(ResourceHandlerRegistry registry) {
5        // Serve from file system with no caching
6        registry.addResourceHandler("/static/**")
7                .addResourceLocations("file:src/main/resources/static/")
8                .setCachePeriod(0);  // No caching in development
9    }
10}

Files served from file: locations are read directly from disk on each request, so edits are visible immediately.

Production: Cache-Busting with Content Hashing

In production, static files should be cached aggressively but invalidated when content changes. Spring's VersionResourceResolver appends a content hash to filenames:

java
1@Configuration
2public class WebConfig implements WebMvcConfigurer {
3    @Override
4    public void addResourceHandlers(ResourceHandlerRegistry registry) {
5        registry.addResourceHandler("/static/**")
6                .addResourceLocations("classpath:/static/")
7                .resourceChain(true)  // Enable resource chain
8                .addResolver(new VersionResourceResolver()
9                    .addContentVersionStrategy("/**"));  // Hash-based versioning
10    }
11}
properties
# application.properties
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**

This transforms URLs like /static/css/style.css into /static/css/style-abc123def.css. When the file changes, the hash changes, and browsers fetch the new version.

Using in Thymeleaf Templates

html
1<!-- Thymeleaf automatically resolves versioned URLs -->
2<link rel="stylesheet" th:href="@{/static/css/style.css}" />
3<!-- Renders as: /static/css/style-abc123def.css -->
4
5<script th:src="@{/static/js/app.js}"></script>
6<!-- Renders as: /static/js/app-xyz789ghi.js -->

Using ResourceUrlProvider in JSP

java
1@ControllerAdvice
2public class ResourceUrlAdvice {
3
4    @Autowired
5    private ResourceUrlProvider resourceUrlProvider;
6
7    @ModelAttribute("urls")
8    public ResourceUrlProvider urls() {
9        return resourceUrlProvider;
10    }
11}
jsp
<link rel="stylesheet" href="${urls.getForLookupPath('/static/css/style.css')}" />

Production: Cache-Control Headers

properties
1# Cache static resources for 1 year (versioned URLs make this safe)
2spring.web.resources.cache.cachecontrol.max-age=365d
3
4# Or configure per resource type
5spring.web.resources.cache.cachecontrol.cache-public=true
java
1@Configuration
2public class WebConfig implements WebMvcConfigurer {
3    @Override
4    public void addResourceHandlers(ResourceHandlerRegistry registry) {
5        registry.addResourceHandler("/static/**")
6                .addResourceLocations("classpath:/static/")
7                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS)
8                    .cachePublic());
9    }
10}

With content-hashed URLs, setting a long cache duration is safe because changed files get new URLs.

Production: Fixed Version Strategy

If content hashing is not suitable, use a fixed version string (e.g., application version):

properties
spring.web.resources.chain.strategy.fixed.enabled=true
spring.web.resources.chain.strategy.fixed.paths=/**
spring.web.resources.chain.strategy.fixed.version=v2.1.0

This transforms /css/style.css into /v2.1.0/css/style.css. Update the version on each deployment.

Profile-Based Configuration

properties
1# application-dev.properties
2spring.web.resources.cache.cachecontrol.no-cache=true
3spring.web.resources.chain.enabled=false
4spring.devtools.livereload.enabled=true
5
6# application-prod.properties
7spring.web.resources.cache.cachecontrol.max-age=365d
8spring.web.resources.chain.strategy.content.enabled=true
9spring.web.resources.chain.strategy.content.paths=/**

Common Pitfalls

  • Forgetting to disable caching in development: Without setCachePeriod(0) or no-cache headers, the browser caches static files and you do not see changes even after restarting. Set spring.web.resources.cache.cachecontrol.no-cache=true for development profiles.
  • Using DevTools in production: Spring DevTools is intended for development only. It is automatically disabled when running from a packaged JAR, but ensure it is declared with <scope>runtime</scope> and <optional>true</optional> to prevent accidental inclusion.
  • Setting long cache durations without content hashing: If you set max-age=365d but do not use versioned URLs, browsers serve stale files until the cache expires. Always pair long cache durations with content hashing or a fixed version strategy.
  • Not using Thymeleaf's @{} syntax for resource URLs: Hardcoding paths like href="/css/style.css" bypasses Spring's resource versioning. Use th:href="@{/css/style.css}" so Spring resolves the versioned URL automatically.
  • Mixing file: and classpath: locations incorrectly: When both are configured, file: takes precedence for matching paths. In production, remove file: locations to ensure static files are served from the packaged JAR, not from a nonexistent file path.

Summary

  • Use Spring DevTools with LiveReload for instant static content updates during development
  • Serve from file: locations to avoid restarts entirely for front-end changes
  • Use VersionResourceResolver with content hashing for production cache-busting
  • Set Cache-Control: max-age=365d with versioned URLs for optimal browser caching
  • Use Spring profiles to separate development (no-cache, DevTools) from production (long cache, versioning) configurations

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

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

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.