Handling Trailing Slashes in Spring 6: Introducing UrlHandlerFilter
Overview
When migrating applications to Spring Framework 6 / Spring Boot 3, many teams encounter unexpected 404 errors due to URL pattern changes—especially around trailing slash handling.
In previous versions of Spring, URLs with or without a trailing slash were treated the same:
/api/users /api/users/
However, Spring 6 no longer supports this behavior by default, requiring explicit handling.
The Problem After Migration
After upgrading:
/api/resource✅ works/api/resource/❌ may return 404
Example:
@GetMapping("/users")
public List<User> getUsers() { ... }
Calling GET /users/ will fail unless handled explicitly.
Common Fixes (and Limitations)
1. Duplicate mappings
@GetMapping({"/users", "/users/"})
✅ Works
❌ Not scalable
2. Enable trailing slash globally
configurer.setUseTrailingSlashMatch(true);
❌ Deprecated in Spring 6
✅ Introducing UrlHandlerFilter
Spring 6 provides a better solution: UrlHandlerFilter.
This filter helps normalize incoming URLs by:
- Removing trailing slashes
- Redirecting URLs
- Standardizing request paths
Example: Remove Trailing Slash
Configuration
@Configuration
public class WebConfig {
@Bean
public UrlHandlerFilter urlHandlerFilter() {
return UrlHandlerFilter
.trimTrailingSlash("/**");
}
}
Result
/users/→/users/orders/123/→/orders/123
✔ No controller changes needed
✔ Centralized solution
Optional: Redirect Instead
UrlHandlerFilter.redirectTrailingSlash("/**");
This will return a 301 redirect:
/users/→/users
When to Use It
- Migrating legacy systems
- Cannot update all clients immediately
- Want centralized handling
Best Practices
- Short-term: Use
trimTrailingSlash - Long-term: Standardize API without trailing slash
Summary
- Duplicate mapping → ❌ Not recommended
- Global trailing slash → ❌ Deprecated
- UrlHandlerFilter → ✅ Best approach
Final Thoughts
Spring 6 introduces stricter URL handling, which improves API clarity but requires careful migration.
UrlHandlerFilter is a clean and scalable solution to handle trailing slashes without modifying every controller.