Spring Boot 2.6 Path Matching Upgrade: Fixing Broken Interceptor Patterns
This article explains how Spring Boot 2.6's switch from AntPathMatcher to PathPatternParser breaks existing path patterns in interceptors and static resources, details the syntax differences, shows how to fix patterns using {*path} capture groups, and provides troubleshooting techniques including actuator/mappings and unit testing with PathPatternParser.
Why the Upgrade Fails
Upgrading a project from Spring Boot 2.5 to 2.6 causes the application to fail at startup with:
***************************
APPLICATION FAILED TO START
***************************
Description:
Invalid mapping pattern detected: /**/open/**/detail
^
No more pattern data allowed after {*...} or ** pattern elementThe culprit is an interceptor allow-list rule addPathPatterns("/api/**/open/**") that worked for two years on 2.5. The error originates from PathPatternParser, which replaces AntPathMatcher as the default path matching implementation in Spring Boot 2.6. The two parsers accept different wildcard syntaxes, and the old patterns scattered across interceptors, static resource mappings, and security configurations now cause startup failures.
How the Two Parsers Differ
AntPathMatcher (Spring Boot < 2.6 default)
Matches paths at runtime by splitting the pattern on / and comparing segment by segment. ** can appear anywhere and matches any number of path segments.
Patterns like /**/detail, /api/**/open/**, /**/*.css all work.
No dedicated syntax for capturing the remainder of a path.
Supports suffix matching (e.g., /user/list.json matches @GetMapping("/user/list")) via useSuffixPatternMatch (deprecated in Spring Framework 5.3, removed in 6.0).
PathPatternParser (Spring Boot ≥ 2.6 default)
Parses each pattern into a syntax tree at startup; incoming requests are converted to a PathContainer and matched against the tree. ** is restricted to the end of the pattern only.
Capturing the remaining path uses {*path}, also only allowed at the end. /**/detail fails because ** is not at the end. /**/*.css fails for the same reason; changing to /*/*.css compiles but only matches exactly two segments (e.g., /static/css/app.css has three segments and won't match).
Suffix matching is not supported.
Moving parsing to startup means invalid patterns are caught immediately rather than at first request.
Comparison of Key Aspects
Default in Boot: AntPathMatcher before 2.6; PathPatternParser 2.6+
Matching approach: AntPathMatcher uses runtime segment-by-segment string comparison; PathPatternParser uses startup parsing to syntax tree.
** position: AntPathMatcher allows anywhere; PathPatternParser only at end.
Capture remainder: AntPathMatcher has no dedicated syntax; PathPatternParser uses {*path} (only at end).
Suffix matching: AntPathMatcher supports (e.g., .json); PathPatternParser does not support.
Interceptor Registration Uses the Same Parser
addPathPatternsand excludePathPatterns on InterceptorRegistry feed strings into the same PathPatternParser instance used by RequestMappingHandlerMapping. WebMvcConfigurationSupport injects the parser when creating the handler mapping and reuses it for the interceptor registry. Therefore interceptor patterns are subject to the same constraints: ** in the wrong position fails at startup.
Key Consequences
/*matches exactly one path segment; /api/user matches but /api/user/1 does not. To cover all sub-paths use /api/**.
Patterns do not include server.servlet.context-path. If the context path is /shop, write /api/** in the interceptor, not /shop/api/**. MVC strips the context path before matching; this differs from Servlet Filter mapping which follows the Servlet spec.
Static resources and actuator endpoints must be explicitly excluded. Example configuration:
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
public WebConfig(AuthInterceptor authInterceptor) {
this.authInterceptor = authInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/static/**", "/error")
.order(1);
}
}Exclude patterns also obey the parser rules; /**/static/** would be rejected at startup.
Silent Failure: preHandle Returns false Without Response
If an interceptor's preHandle returns false but writes nothing to the response, the client receives a 200 OK with an empty body. Browsers show a blank screen, API callers see success, and logs show no exception. This is easily mistaken for a path-matching issue when the path actually matched and the interceptor ran.
Patterns That Don't Error But Don't Work
Trailing Slash Matching
Spring Framework 6.0 changed useTrailingSlashMatch default from true to false; Spring Boot 3.0 inherits this. @GetMapping("/some/greeting") no longer matches /some/greeting/ (returns 404). Many front-end concatenation logic, gateway rewrite rules, and Nginx proxy_pass configurations relied on this equivalence. Official workarounds:
Handle redirection at the proxy layer.
Declare both paths in the controller: @GetMapping({"/some/greeting", "/some/greeting/"}).
Globally re-enable via configurePathMatch setting setUseTrailingSlashMatch(true) (deprecated in 6.0); the replacement is PathPatternParser.setMatchOptionalTrailingSeparator(true) which requires replacing the default parser.
Actuator Endpoints Use a Separate Matcher
Spring Boot 2.6 release notes state Actuator endpoints also switched to PathPattern matching, and this strategy is not controlled by spring.mvc.pathmatch.matching-strategy. Even if MVC is set back to ant_path_matcher, Actuator still uses the new rules. With management.server.port configured, Actuator runs in a separate child container; interceptors registered via WebMvcConfigurer do not apply to it. Securing Actuator requires a SecurityFilterChain or separate configuration on the management port.
Springfox 2.x Incompatibility
Springfox 2.x internally depends on AntPathMatcher.getPatternsCondition. When the mapping strategy is PathPattern, it receives PathPattern-based mapping information, fails to extract values, and throws a NullPointerException at documentationPluginsBootstrapper. The proper fix is migrating to Springdoc; a temporary workaround is switching the matching strategy back to ant_path_matcher.
Cost of Reverting to AntPathMatcher
A single configuration line restores the old behavior:
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcherThe property accepts ant_path_matcher, ant-path-matcher, or ANT_PATH_MATCHER (relaxed binding). A common typo is spring.mvc.pathpattern.matching-strategy (an old non-existent name) which silently does nothing.
Trade-offs of reverting:
MVC routing falls back to string comparison, which is slower than the syntax tree but rarely noticeable at typical business QPS.
Actuator does not revert; it continues using PathPattern, leading to two different rule sets in the same application.
The configuration persists; new developers may write Ant-style patterns like /**/xx that work locally but fail in environments without the config, causing confusing startup failures later.
Recommended Approach: Update Patterns, Not Strategy
Replace /**/open/** style patterns with {*path} capture groups. This makes semantics clearer and provides access to the matched remainder:
// Captures everything after /api; the path variable contains the remainder
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/{*path}")
.excludePathPatterns("/api/open/{*path}");The captured path value includes a leading slash. For /api/open/user/detail, path is /open/user/detail. This can be used for audit logging or permission decisions, adding information that ** alone cannot provide.
Troubleshooting Techniques
Startup Failures
Search logs for Invalid mapping pattern detected; the caret points to the offending ** or {*...} with trailing characters. Check these common locations:
Interceptor addPathPatterns /
excludePathPatterns addResourceHandlers spring.mvc.static-path-patternRuntime 404s
Inspect /actuator/mappings which lists all entries in RequestMappingHandlerMapping. The patterns field shows the parsed result. If the expected path is missing, the issue is at mapping registration. If present but still 404, the problem lies in interceptors or outer filters.
Unit Testing Patterns Without Starting the App
PathPatternParsercan be used directly in tests:
PathPatternParser parser = new PathPatternParser();
PathPattern pattern = parser.parse("/api/**");
// Second argument is PathContainer, not a plain string
assertTrue(pattern.matches(PathContainer.parsePath("/api/user/1")));
assertFalse(pattern.matches(PathContainer.parsePath("/other/user/1")));Run all project patterns through such tests before upgrading to catch incompatible patterns early, especially when interceptors and security config are spread across modules.
Root Cause Summary
The rules themselves are not complex. The difficulty stems from their dispersion: route annotations, interceptors, static resource mappings, security config, and actuator endpoints each parse paths, sometimes with different strategies. Spring Boot 2.6 and 3.0 each changed half the stack, leaving a mix of old and new rules whose effective behavior depends on startup parameters and dependency versions. This inconsistency is why these issues are time-consuming to diagnose.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
