{"record":{"id":"ef4271dfc0863764","repo":"spring-projects/spring-security","slug":"a-universal-match-pattern-is-defined-befor","errorCode":null,"errorMessage":"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","messagePattern":"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","errorType":"exception","errorClass":"UnreachableFilterChainException","httpStatus":null,"severity":"error","filePath":"config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java","lineNumber":89,"sourceCode":"\n\t@Override\n\tpublic void validate(FilterChainProxy fcp) {\n\t\tfor (SecurityFilterChain filterChain : fcp.getFilterChains()) {\n\t\t\tcheckLoginPageIsntProtected(fcp, filterChain.getFilters());\n\t\t\tcheckFilterStack(filterChain.getFilters());\n\t\t}\n\t\tcheckPathOrder(new ArrayList<>(fcp.getFilterChains()));\n\t\tcheckForDuplicateMatchers(new ArrayList<>(fcp.getFilterChains()));\n\t\tcheckAuthorizationFilters(new ArrayList<>(fcp.getFilterChains()));\n\t}\n\n\tprivate void checkPathOrder(List<SecurityFilterChain> filterChains) {\n\t\t// Check that the universal pattern is listed at the end, if at all\n\t\tIterator<SecurityFilterChain> chains = filterChains.iterator();\n\t\twhile (chains.hasNext()) {\n\t\t\tif (chains.next() instanceof DefaultSecurityFilterChain securityFilterChain) {\n\t\t\t\tif (AnyRequestMatcher.INSTANCE.equals(securityFilterChain.getRequestMatcher()) && chains.hasNext()) {\n\t\t\t\t\tthrow new UnreachableFilterChainException(\"A universal match pattern ('/**') is defined \"\n\t\t\t\t\t\t\t+ \" before other patterns in the filter chain, causing them to be ignored. Please check the \"\n\t\t\t\t\t\t\t+ \"ordering in your <security:http> namespace or FilterChainProxy bean configuration\",\n\t\t\t\t\t\t\tsecurityFilterChain, chains.next());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {\n\t\tDefaultSecurityFilterChain filterChain = null;\n\t\tfor (SecurityFilterChain chain : chains) {\n\t\t\tif (filterChain != null) {\n\t\t\t\tif (chain instanceof DefaultSecurityFilterChain defaultChain) {\n\t\t\t\t\tif (defaultChain.getRequestMatcher().equals(filterChain.getRequestMatcher())) {\n\t\t\t\t\t\tthrow new UnreachableFilterChainException(\n\t\t\t\t\t\t\t\t\"The FilterChainProxy contains two filter chains using the\" + \" matcher \"\n\t\t\t\t\t\t\t\t\t\t+ defaultChain.getRequestMatcher()\n\t\t\t\t\t\t\t\t\t\t+ \". If you are using multiple <http> namespace \"","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move the universal-match ('/**' or anyRequest()) chain to the END of the chain list","Put more specific patterns (e.g. /api/**, /admin/**) before the universal pattern in the XML or builder","In programmatic config, order SecurityFilterChain beans with @Order or by registration order so /** is last"],"exampleFix":"// before\n<http pattern=\"/**\" .../>\n<http pattern=\"/api/**\" .../>\n// after\n<http pattern=\"/api/**\" .../>\n<http pattern=\"/**\" .../>","handlingStrategy":"validation","validationCode":"List<String> patterns = chains.stream().map(SecurityFilterChain::toString).toList();\nint universalIdx = patterns.indexOf(\"/**\"); // or track matchers\nif (universalIdx != -1 && universalIdx < patterns.size() - 1) throw new IllegalStateException(\"/** chain must be last\");","typeGuard":null,"tryCatchPattern":"try {\n    filterChainProxy.afterPropertiesSet();\n} catch (UnreachableFilterChainException e) {\n    logger.error(\"Reorder chains: {} (unreachable: {})\", e.getMessage(), e.getSecond());\n}","preventionTips":["Always declare the /** or anyRequest() chain last","Use @Order on programmatic SecurityFilterChain beans","Add a startup check that specific patterns precede the universal one"],"tags":["spring-security","filter-chain","ordering","startup-validation"],"backgroundTag":"invalid-config-value","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}