spring-projects/spring-security · error · ServerExchangeRejectedException
The request was rejected because the URL was not normalized
Error message
The request was rejected because the URL was not normalized
What it means
StrictServerWebExchangeFirewall (WebFlux equivalent of StrictHttpFirewall) checks that the request URL is normalized — no directory traversal segments like /../, double slashes, or dot segments. If isNormalized(URI) fails, it throws ServerExchangeRejectedException and refuses the exchange. This blocks path-traversal and URL-obfuscation attacks at the edge of the filter chain.
Solutions
- Find the offending URL in the exception/access logs and fix the client to send normalized paths.
- Normalize at the proxy: configure nginx/Apache to merge slashes and resolve dot segments before forwarding.
- Re-check any recent proxy or gateway config changes that may double-encode paths (e.g. proxy_pass without proper decoding).
- As a last resort relax specific checks (setAllowUrlEncodedDoubleSlash / setStrictHttpFirewall equivalents on the ServerWebExchange firewall bean), understanding the security tradeoff.
Example fix
// before // client calls GET /api//v1/../v1/users // after // client calls GET /api/v1/users // and/or configure the proxy to normalize: // nginx: merge_slashes on; before proxy_pass;
Defensive patterns
Strategy: validation
Validate before calling
// Client-side normalization guard (Java)
URI uri = URI.create(rawUrl).normalize();
if (rawUrl.contains("../") || rawUrl.contains("//") || !uri.getPath().equals(new URI(uri.normalize()).getPath())) {
throw new IllegalArgumentException("URL must be normalized: " + rawUrl);
} Try / catch
// WebFlux error handling
@Component
class RejectedExchangeHandler implements WebExceptionHandler {
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (ex instanceof ServerExchangeRejectedException) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
return Mono.error(ex);
}
} Prevention
- Enable path merging/normalization on reverse proxies
- Never concatenate user input into URL paths unvalidated
- Monitor rejected-request logs for probing activity
- Keep clients on library-generated URIs (UriComponentsBuilder.normalize)
When it happens
Trigger: A request arrives whose path contains non-normalized segments: /../, /./, double slashes (except after scheme), backslashes, or encoded equivalents that Spring's UriComponentsBuilder normalization flags.
Common situations: Scanners/probing tools sending traversal-style URLs; front-end proxies rewriting paths incorrectly (e.g. double-encoding); clients appending extra slashes or dot segments; legacy clients with hand-built URLs.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- The request was rejected because the URL contained a…
- The request was rejected because the HTTP method
- The request was rejected because the URL was not normalized.
- The was rejected because it can only contain printable…
- Un-normalized paths are not supported: " +…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/23a6462bae30c0d2.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/server/firewall/StrictServerWebExchangeFirewall.java:195
}
public Set<String> getEncodedUrlBlocklist() {
return this.encodedUrlBlocklist;
}
public Set<String> getDecodedUrlBlocklist() {
return this.decodedUrlBlocklist;
}
@Override
public Mono<ServerWebExchange> getFirewalledExchange(ServerWebExchange exchange) {
return Mono.fromCallable(() -> {
ServerHttpRequest request = exchange.getRequest();
rejectForbiddenHttpMethod(request);
rejectedBlocklistedUrls(request);
rejectedUntrustedHosts(request);
if (!isNormalized(request)) {
throw new ServerExchangeRejectedException(
"The request was rejected because the URL was not normalized");
}
exchange.getResponse().beforeCommit(() -> Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
HttpHeaders headers = response.getHeaders();
headers.forEach((headerName, headerValues) -> {
for (String headerValue : headerValues) {
validateCrlf(headerName, headerValue);
}
});
}));
return new StrictFirewallServerWebExchange(exchange);
});
}
private static void validateCrlf(String name, String value) {
Assert.isTrue(!hasCrlf(name) && !hasCrlf(value), () -> "Invalid characters (CR/LF) in header " + name);View on GitHub (pinned to 96852e8860)