Skip to content

Fixes overlapping X-Forwarded-Prefix for chained path filters [server mvc] - #4238

Open
garvit-joshi wants to merge 1 commit into
spring-cloud:mainfrom
garvit-joshi:fix/gh-4237-xforwarded-prefix-overlap-mvc
Open

Fixes overlapping X-Forwarded-Prefix for chained path filters [server mvc]#4238
garvit-joshi wants to merge 1 commit into
spring-cloud:mainfrom
garvit-joshi:fix/gh-4237-xforwarded-prefix-overlap-mvc

Conversation

@garvit-joshi

@garvit-joshi garvit-joshi commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Fixes gh-4237

When a Server MVC route applies more than one path-mutating filter (e.g. StripPrefix=1 twice), every filter function adds a snapshot of the request URL to MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR, and XForwardedRequestHeadersFilter emitted one X-Forwarded-Prefix value per suffix-matching snapshot. For a request to /tenant/api/blue stripped to /blue, the downstream request carried:

X-Forwarded-Prefix: /tenant/api,/api

Downstream forwarded-header processing concatenates comma-separated prefix values, so the downstream application reconstructs a wrong context path (/tenant/api/api instead of /tenant/api).

With this change the prefix is derived once, from the URL as received from the client (the first entry of the attribute, which is insertion-ordered) against the final routed path. A single gateway therefore contributes a single prefix value. Values appended by upstream proxies are still preserved, covered by a new test asserting /upstream,/tenant/api.

This is the Server MVC counterpart of gh-4236 (WebFlux), which is fixed separately so each module can be reviewed and backported independently.

… mvc]

When multiple path-mutating filter functions (e.g. two StripPrefix
filters) process a request, each adds a snapshot to
MvcUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR and
XForwardedRequestHeadersFilter emitted one prefix per snapshot,
producing overlapping values such as "/tenant/api,/api". Downstream
forwarded-header processing concatenates the values and reconstructs a
wrong context path ("/tenant/api/api").

The prefix is now derived once, from the URL as received from the
client (the first entry of the attribute) against the final routed
path, so a single gateway contributes a single prefix while values
appended by upstream proxies are preserved.

Fixes spring-cloudgh-4237

Signed-off-by: Garvit Joshi <garvitjoshi9@gmail.com>
@garvit-joshi

Copy link
Copy Markdown
Contributor Author

Additional context on why the duplicate X-Forwarded-Prefix values cause an incorrect downstream context path:

A comma in an HTTP field represents a list separator; it does not itself mean path concatenation. X-Forwarded-Prefix is a non-standard header, so its interpretation is implementation-specific.

Spring MVC performs the concatenation in ForwardedHeaderFilter.ForwardedPrefixExtractor#initForwardedPrefix. It splits the header on commas and appends every prefix:

String[] rawPrefixes = StringUtils.tokenizeToStringArray(result, ",");
for (String rawPrefix : rawPrefixes) {
	// trailing slash handling omitted
	prefix.append(rawPrefix);
}

Source: https://github.com/spring-projects/spring-framework/blob/v7.0.3/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java#L395-L415

Therefore:

X-Forwarded-Prefix: /tenant/api,/api

is interpreted as:

/tenant/api + /api = /tenant/api/api

The second /api is not a prefix contributed by another proxy; it is an intermediate URL snapshot produced by the second path-mutating filter in the same gateway. This change correctly makes the gateway contribute its complete prefix once (/tenant/api), while still preserving a legitimate upstream value such as /upstream,/tenant/api.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Server MVC] XForwardedRequestHeadersFilter emits overlapping X-Forwarded-Prefix values for chained path filters

2 participants