spring-projects/spring-security · error · RequestRejectedException

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

StrictHttpFirewall rejects any request whose URL is not normalized: it checks the requestURI/contextPath/servletPath/pathInfo for dot-segments ('/./', '/../'), duplicate slashes, and other non-canonical forms. This is a deliberate hardening against path-traversal and authorization-bypass attacks; the request is rejected with RequestRejectedException before filtering.

Solutions

  1. Fix the URL at the source: normalize/rewrite at your reverse proxy (nginx: merge_slashes on; reject '..' patterns) or in the client/router
  2. Find the client producing un-normalized URLs from access logs and fix its URL construction (proper path joining instead of string concatenation)
  3. If a specific pattern is safe and required, use StrictHttpFirewall's configuration instead of reverting to DefaultHttpFirewall: e.g. setAllowUrlEncodedDoubleSlash/setAllowUrlEncodedPercent — note there is no option to allow dot-segments, so those must be normalized upstream
  4. Wrap with a custom firewall/filter that normalizes the request (HttpFirewall wrapper or Spring Cloud Gateway rewrite filter) before it hits StrictHttpFirewall

Example fix

// before
String url = baseUrl + "/" + path; // path may start with '/' or contain './'
// after
String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
    .path(path)
    .build().normalize().toUriString();
Defensive patterns

Strategy: try-catch

Validate before calling

String uri = request.getRequestURI();
boolean normalized = uri.equals(URI.create(uri).normalize().getPath()) && !uri.contains("//");

Try / catch

try {
    FirewalledRequest fw = strictFirewall.getFirewalledRequest(request);
    chain.doFilter(fw, response);
} catch (RequestRejectedException e) {
    audit.log("Rejected non-normalized URL", request.getRequestURI(), e.getMessage());
    response.sendError(HttpServletResponse.SC_BAD_REQUEST);
}

Prevention

When it happens

Trigger: getFirewalledRequest sees a URI like '/a/../b', '/a/./b', '/a//b', or containing encoded dot variants; also fired when trailing '..' or '.' segments survive. Any request through FilterChainProxy with such a path is rejected.

Common situations: Crawlers or misbehaving clients sending dot-segment URLs; double-encoding by proxies turning %2e%2e into '..'; apps behind proxies that don't normalize; legitimate deep links with '//' from string-concatenated base URLs; frameworks generating '/foo/./bar' links.

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


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

Appendix: source

Thrown at web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java:521

	}

	private void urlBlocklistsAddAll(Collection<String> values) {
		this.encodedUrlBlocklist.addAll(values);
		this.decodedUrlBlocklist.addAll(values);
	}

	private void urlBlocklistsRemoveAll(Collection<String> values) {
		this.encodedUrlBlocklist.removeAll(values);
		this.decodedUrlBlocklist.removeAll(values);
	}

	@Override
	public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {
		rejectForbiddenHttpMethod(request);
		rejectedBlocklistedUrls(request);
		rejectedUntrustedHosts(request);
		if (!isNormalized(request)) {
			throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
		}
		rejectNonPrintableAsciiCharactersInFieldName(request.getRequestURI(), "requestURI");
		return new StrictFirewalledRequest(request);
	}

	private void rejectNonPrintableAsciiCharactersInFieldName(String toCheck, String propertyName) {
		if (!containsOnlyPrintableAsciiCharacters(toCheck)) {
			throw new RequestRejectedException(String
				.format("The %s was rejected because it can only contain printable ASCII characters.", propertyName));
		}
	}

	private void rejectForbiddenHttpMethod(HttpServletRequest request) {
		if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {
			return;
		}
		if (!this.allowedHttpMethods.contains(request.getMethod())) {
			throw new RequestRejectedException(

View on GitHub (pinned to 96852e8860)