spring-projects / spring-projects/spring-security

HeaderWriterFilter headers not written on RequestDispatcher.forward() under Tomcat 11

Open
#19,457 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage type: bug
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Describe the bug
Spring Security's default response headers (Cache-Control, X-Content-Type-Options, X-XSS-Protection, X-Frame-Options, …) are not written on responses produced through a servlet RequestDispatcher.forward() when running on Tomcat 11. Hitting the same servlet directly returns all headers; only forwarded responses lose them, silently.

The trigger is Tomcat 11's changed default for the Context attribute suspendWrappedResponseAfterForward (false in Tomcat 10.1.x → true in Tomcat 11): after a forward, Catalina suspends/unwraps the wrapped response instead of closing it, so HeaderWriterFilter's OnCommittedResponseWrapper never receives its on-commit callback. The behavior tracks the Tomcat major version and is independent of the Spring Boot and Spring Security versions.

Setting HeaderWriterFilter.setShouldWriteHeadersEagerly(true) (or restoring suspendWrappedResponseAfterForward=false) works around it. Raising it here since HeaderWriterFilter is the component that silently omits the headers: should its default be made robust to Tomcat 11's behavior, or at least documented?

To Reproduce
The sample is a Spring Boot app with Spring Security (permitAll) and a servlet (TargetServlet) mapped at /target/*. It exposes the same servlet three ways (Application.java): directly (/target/*), via a URL forward request.getRequestDispatcher(...).forward(...) (/forward-url), and via a named-dispatcher forward using ServletForwardingController (/forward-named).

mvn clean package
java -jar target/ss-forward-mre-1.0.jar

curl -sD - -o /dev/null http://localhost:8090/target/direct    # headers PRESENT
curl -sD - -o /dev/null http://localhost:8090/forward-url      # headers MISSING
curl -sD - -o /dev/null http://localhost:8090/forward-named    # headers MISSING

Rebuilding against Tomcat 10.1.x (mvn clean package -Dtomcat.version=10.1.39) makes the forwarded responses keep the headers.
Likewise, on Tomcat 11 setting suspendWrappedResponseAfterForward=false restores them; run with --tomcat.suspend-fix=true (the sample wires this flag through a WebServerFactoryCustomizer).

Expected behavior
Responses produced via forward() carry the same Spring Security response headers as direct responses (the Tomcat 10.1 / Spring Boot 3.x behavior).

Sample

https://github.com/mcollovati/spring-security-forward-headers-tomcat11

Environment: Spring Boot 4.1.0, Spring Security 7.1.0, Tomcat 11.0.22, Java 21.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with HeaderWriterFilter and its OnCommittedResponseWrapper behavior, then reproduce the issue using the linked sample's Application.java. Run mvn clean package and the three curl requests on Tomcat 11, comparing direct and forwarded responses. Done means forwarded responses retain the same Spring Security headers as direct responses without relying on the documented workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.