Describe the bug
When a route handler returns a ServerResponse created via ServerResponse.ok().build(). The headers exposed to ServerResponse.headers() are immutable (ReadOnlyHttpHeaders).
Attempting to modify response headers inside an After Filter (even with predefined AfterFilterFunctions.addResponseHeader() function) throws UnsupportedOperationException with stacktrace:
java.lang.UnsupportedOperationException
at org.springframework.http.ReadOnlyHttpHeaders.addAll(ReadOnlyHttpHeaders.java:102) ~[spring-web-6.2.18.jar:6.2.18]
at org.springframework.cloud.gateway.server.mvc.filter.AfterFilterFunctions.lambda$addResponseHeader$0(AfterFilterFunctions.java:59) ~[spring-cloud-gateway-server-mv
c-4.3.4.jar:4.3.4]
--- snip ---
However, the same .after() filter works correctly when using predefined handlers such as HandlerFunctions.http()
Expected behavior
AfterFilterFunctions.addResponseHeader() (and any After Filter Functions that modifies headers) should be able to modify response headers consistently for ServerResponse returned from route handlers.
Alternatively, if immutable headers are intentional, the framework should avoid mutating response.headers() directly inside AfterFilterFunctions.addResponseHeader().
Sample
Minimal, reproducible code example
fooRoute (failing route) returns 500 Internal Server Error
public RouterFunction<ServerResponse> fooRoute() {
return route("fooRoute")
.GET(path("/foo"), req -> ServerResponse.ok().build())
.after(addResponseHeader("X-Test", "TestFoo"))
.build();
}
barRoute (working route) returns 200 OK with expected response header
public RouterFunction<ServerResponse> barRoute() {
return route("barRoute")
.GET(path("/bar"), http())
.before(uri("http://httpbin.org"))
.before(setPath("/get"))
.after(addResponseHeader("X-Test", "TestBar"))
.build();
}
Java version: 21
Spring boot: 3.5.14
Spring cloud version: 2025.0.2
Spring cloud gateway web MVC: 4.3.4
This issue appears specific to response created directly from route handlers, since proxied responses trough HandlerFunctions.http() behave as expected.
Describe the bug
When a route handler returns a
ServerResponsecreated viaServerResponse.ok().build(). The headers exposed toServerResponse.headers()are immutable (ReadOnlyHttpHeaders).Attempting to modify response headers inside an After Filter (even with predefined
AfterFilterFunctions.addResponseHeader()function) throwsUnsupportedOperationExceptionwith stacktrace:However, the same
.after()filter works correctly when using predefined handlers such asHandlerFunctions.http()Expected behavior
AfterFilterFunctions.addResponseHeader()(and any After Filter Functions that modifies headers) should be able to modify response headers consistently forServerResponsereturned from route handlers.Alternatively, if immutable headers are intentional, the framework should avoid mutating
response.headers()directly insideAfterFilterFunctions.addResponseHeader().Sample
Minimal, reproducible code example
fooRoute(failing route) returns 500 Internal Server ErrorbarRoute(working route) returns 200 OK with expected response headerJava version: 21
Spring boot: 3.5.14
Spring cloud version: 2025.0.2
Spring cloud gateway web MVC: 4.3.4
This issue appears specific to response created directly from route handlers, since proxied responses trough
HandlerFunctions.http()behave as expected.