spring-projects/spring-security · error · ServerExchangeRejectedException
The request was rejected because the domain
Error message
The request was rejected because the domain <hostName> is untrusted.
What it means
The firewall extracts the Host from the request URI and tests it against the configured allowedHostnames predicate. If the host is not allowed, the request is rejected with ServerExchangeRejectedException. This defends against host-header injection and cache-poisoning attacks.
Solutions
- Configure the firewall's allowedHostnames to include every legitimate hostname: firewall.setAllowedHostnames(hostname -> allowedSet.contains(hostname)).
- Add the new environment's hostname (staging/internal DNS) to the allowlist in configuration.
- Fix proxy/load-balancer settings that pass through the wrong Host header (use proxy_set_header Host $host).
- If intentional probing, block the client and ignore the rejection.
Example fix
// before: no host check configured, default rejects everything unusual
StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
// after
firewall.setAllowedHostnames(host ->
host.equals("example.com") || host.equals("staging.example.com")); Defensive patterns
Strategy: validation
Validate before calling
Set<String> allowed = Set.of("example.com", "staging.example.com");
boolean isHostAllowed(String host) {
return host != null && allowed.contains(host.toLowerCase());
} Try / catch
try {
exchange = firewall.getFirewalledExchange(exchange);
} catch (ServerExchangeRejectedException e) {
log.warn("Untrusted host rejected: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
} Prevention
- Explicitly set setAllowedHostnames with every environment's hostname (dev, staging, prod).
- Include localhost/IP variants only in non-production profiles.
- Ensure load balancers preserve the original Host header.
- Add a startup check that logs the configured host allowlist.
When it happens
Trigger: A request arrives whose Host header (request.getURI().getHost()) fails the allowedHostnames predicate — e.g. firewall with no allowedHostnames configured (or a restrictive allowlist) receiving Host: <hostName> from a client, load balancer, or direct IP access.
Common situations: Accessing the app via localhost/IP/cluster-internal DNS while the allowlist only contains the public domain; forgetting to call setAllowedHostnames on the StrictServerWebExchangeFirewall bean; new staging environments with different hostnames; attackers probing with forged Host headers (correctly blocked).
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
- The request was rejected because the header name
- The request was rejected because the parameter name
- The request was rejected because the URL was not normalized.
- The requestURI cannot contain encoded slash. Got " +…
- The was rejected because it can only contain printable…
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/d6d185bc936fce42.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/server/firewall/StrictServerWebExchangeFirewall.java:603
if (encodedUrlContains(request, forbidden)) {
throw new ServerExchangeRejectedException(
"The request was rejected because the URL contained a potentially malicious String \""
+ forbidden + "\"");
}
}
for (String forbidden : this.decodedUrlBlocklist) {
if (decodedUrlContains(request, forbidden)) {
throw new ServerExchangeRejectedException(
"The request was rejected because the URL contained a potentially malicious String \""
+ forbidden + "\"");
}
}
}
private void rejectedUntrustedHosts(ServerHttpRequest request) {
String hostName = request.getURI().getHost();
if (hostName != null && !this.allowedHostnames.test(hostName)) {
throw new ServerExchangeRejectedException(
"The request was rejected because the domain " + hostName + " is untrusted.");
}
}
private static Set<HttpMethod> createDefaultAllowedHttpMethods() {
Set<HttpMethod> result = new HashSet<>();
result.add(HttpMethod.DELETE);
result.add(HttpMethod.GET);
result.add(HttpMethod.HEAD);
result.add(HttpMethod.OPTIONS);
result.add(HttpMethod.PATCH);
result.add(HttpMethod.POST);
result.add(HttpMethod.PUT);
return result;
}
private boolean isNormalized(ServerHttpRequest request) {
if (!isNormalized(request.getPath().value())) {View on GitHub (pinned to 96852e8860)