spring-projects/spring-security · error · UnreachableFilterChainException

A universal match pattern ('/**') is defined before other…

Error message

A universal match pattern ('/**') is defined before other patterns in the filter chain, causing them to be ignored. Please check the ordering in your <security:http> namespace or FilterChainProxy bean configuration

What it means

DefaultFilterChainValidator.checkPathOrder validates the FilterChainProxy's chain list after startup. A DefaultSecurityFilterChain whose RequestMatcher is the universal matcher (/**) placed before other chains makes all following chains unreachable, so the validator throws UnreachableFilterChainException to fail fast instead of silently ignoring those chains.

Solutions

  1. Move the universal-match ('/**' or anyRequest()) chain to the END of the chain list
  2. Put more specific patterns (e.g. /api/**, /admin/**) before the universal pattern in the XML or builder
  3. In programmatic config, order SecurityFilterChain beans with @Order or by registration order so /** is last

Example fix

// before
<http pattern="/**" .../>
<http pattern="/api/**" .../>
// after
<http pattern="/api/**" .../>
<http pattern="/**" .../>
Defensive patterns

Strategy: validation

Validate before calling

List<String> patterns = chains.stream().map(SecurityFilterChain::toString).toList();
int universalIdx = patterns.indexOf("/**"); // or track matchers
if (universalIdx != -1 && universalIdx < patterns.size() - 1) throw new IllegalStateException("/** chain must be last");

Try / catch

try {
    filterChainProxy.afterPropertiesSet();
} catch (UnreachableFilterChainException e) {
    logger.error("Reorder chains: {} (unreachable: {})", e.getMessage(), e.getSecond());
}

Prevention

When it happens

Trigger: filterChains contains a chain with AnyRequestMatcher.INSTANCE followed by at least one more chain — e.g. multiple <http> elements where the pattern='/**' one is declared first, or programmatic addFilterChain("/**", ...) before more specific chains.

Common situations: Appending new <http> blocks at the top of the XML instead of the end; building chains programmatically in the wrong order; Spring Boot SecurityFilterChain beans ordered so the catch-all comes first.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ef4271dfc0863764. Report an issue: GitHub.

Appendix: source

Thrown at config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java:89

	@Override
	public void validate(FilterChainProxy fcp) {
		for (SecurityFilterChain filterChain : fcp.getFilterChains()) {
			checkLoginPageIsntProtected(fcp, filterChain.getFilters());
			checkFilterStack(filterChain.getFilters());
		}
		checkPathOrder(new ArrayList<>(fcp.getFilterChains()));
		checkForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));
		checkAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));
	}

	private void checkPathOrder(List<SecurityFilterChain> filterChains) {
		// Check that the universal pattern is listed at the end, if at all
		Iterator<SecurityFilterChain> chains = filterChains.iterator();
		while (chains.hasNext()) {
			if (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {
				if (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {
					throw new UnreachableFilterChainException("A universal match pattern ('/**') is defined "
							+ " before other patterns in the filter chain, causing them to be ignored. Please check the "
							+ "ordering in your <security:http> namespace or FilterChainProxy bean configuration",
							securityFilterChain, chains.next());
				}
			}
		}
	}

	private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
		DefaultSecurityFilterChain filterChain = null;
		for (SecurityFilterChain chain : chains) {
			if (filterChain != null) {
				if (chain instanceof DefaultSecurityFilterChain defaultChain) {
					if (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {
						throw new UnreachableFilterChainException(
								"The FilterChainProxy contains two filter chains using the" + " matcher "
										+ defaultChain.getRequestMatcher()
										+ ". If you are using multiple <http> namespace "

View on GitHub (pinned to 96852e8860)