Skip to content

Cannot modify headers on After Filter when response is created with ServerResponse.ok().build() #4168

@dibalikpohon

Description

@dibalikpohon

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

  1. 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();
}
  1. 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions