diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java b/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java
index af8ef160c2c7..1614671fb9f7 100644
--- a/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java
+++ b/spring-web/src/main/java/org/springframework/web/server/adapter/ForwardedHeaderTransformer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,7 @@
* in which case it removes but does not use the headers.
*
* @author Rossen Stoyanchev
+ * @author Sebastien Deleuze
* @since 5.1
* @see https://tools.ietf.org/html/rfc7239
* @see Forwarded Headers
@@ -165,7 +166,7 @@ private static String getForwardedPrefix(ServerHttpRequest request) {
String[] rawPrefixes = StringUtils.tokenizeToStringArray(header, ",");
for (String rawPrefix : rawPrefixes) {
int endIndex = rawPrefix.length();
- while (endIndex > 1 && rawPrefix.charAt(endIndex - 1) == '/') {
+ while (endIndex > 0 && rawPrefix.charAt(endIndex - 1) == '/') {
endIndex--;
}
prefix.append((endIndex != rawPrefix.length() ? rawPrefix.substring(0, endIndex) : rawPrefix));
diff --git a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java
index 32ad293236e7..576d89153309 100644
--- a/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java
+++ b/spring-web/src/test/java/org/springframework/web/filter/ForwardedHeaderFilterTests.java
@@ -48,6 +48,7 @@
* @author EddĂș MelĂ©ndez
* @author Rob Winch
* @author Brian Clozel
+ * @author Sebastien Deleuze
*/
class ForwardedHeaderFilterTests {
@@ -442,6 +443,15 @@ void shouldConcatenatePrefixesWithTrailingSlashes() throws Exception {
assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/first/second/mvc-showcase");
}
+ @Test
+ void shouldRemoveSingleTrailingSlash() throws Exception {
+ request.addHeader(X_FORWARDED_PREFIX, "/prefix,/");
+ request.setRequestURI("/mvc-showcase");
+
+ HttpServletRequest actual = filterAndGetWrappedRequest();
+ assertThat(actual.getRequestURL().toString()).isEqualTo("http://localhost/prefix/mvc-showcase");
+ }
+
@Test
void requestURLNewStringBuffer() throws Exception {
request.addHeader(X_FORWARDED_PREFIX, "/prefix/");
diff --git a/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java b/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java
index 564eab48b2c6..c1244707a741 100644
--- a/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java
+++ b/spring-web/src/test/java/org/springframework/web/server/adapter/ForwardedHeaderTransformerTests.java
@@ -32,6 +32,7 @@
* Tests for {@link ForwardedHeaderTransformer}.
*
* @author Rossen Stoyanchev
+ * @author Sebastien Deleuze
*/
class ForwardedHeaderTransformerTests {
@@ -170,6 +171,17 @@ void shouldConcatenatePrefixesWithTrailingSlashes() {
assertForwardedHeadersRemoved(request);
}
+ @Test // gh-33465
+ void shouldRemoveSingleTrailingSlash() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.add("X-Forwarded-Prefix", "/prefix,/");
+ ServerHttpRequest request = this.requestMutator.apply(getRequest(headers));
+
+ assertThat(request.getURI()).isEqualTo(URI.create("https://example.com/prefix/path"));
+ assertThat(request.getPath().value()).isEqualTo("/prefix/path");
+ assertForwardedHeadersRemoved(request);
+ }
+
@Test
void forwardedForNotPresent() {
HttpHeaders headers = new HttpHeaders();