Re: Tomcat 9.0.37 - Request Header Parsing Exception: X-Forwarded-For Lost and Host Field Duplicated Resulting in 400 Bad Request
Christopher Schultz <[email protected]> Wed, 1 Apr 2026 09:20:48 -0400
| Newsgroups | gmane.comp.jakarta.tomcat.user |
|---|---|
| Message-ID | <[email protected]> |
扛起一片天!✨,
On 4/1/26 5:33 AM, 扛起一片天!✨ wrote:
> Tomcat version: 9.0.37
> Operating system: windows 11
> JDK version: OpenJDK 1.8
> Problem Description:
> During the debugging process, it was found that in the prepareRequest stage when Tomcat parses the HTTP request headers, the X-Forwarded-For field is lost, and at the same time, there are duplicate Host fields.
> The original request stream (from inputBuffer): --------------------------------------------------
> GET /v1/webapi/server/current_time? t=1774661427160 HTTP/1.1 isdisplaypage:true
> tenantid:
> groupid:
> unity-access-token:f7e20127-1b55-4e1e-a0e2-d15b2caa112ee
> devicetype:
> user-agent:HolliView
> accept:application/json, text/plain, */*
> content-type:application/json
> token:f7e20127-1b55-4e1e-a0e2-d15b2caa112e
> projectid:
> Referer: http://12.52.41.10:8080/hv-visio/display.html? ...
> accept-encoding:gzip, deflate
> accept-language:zh-CN
> forwarded:proto=http; host="12.52.41.10:8080"; for="12.52.41.20:56611"
> x-forwarded-for: 12.52.41.200 x-forwarded-proto:http
> x-forwarded-prefix:/hv-data
> x-forwarded-port:8080
> x-forwarded-host: 12.52.41.10:8080
> host: 127.0.0.1:80011 content-length:0
> --------------------------------------------------
>
>
> Actual parsing result (MimeHeaders):
> - X-Forwarded-For field: Missing (does not exist)
> - Host field: Appears twice (repeated)
> headers=@MimeHeaderField[][
> @MimeHeaderField[
> nameB=@MessageBytes[isdisplaypage],
> valueB=@MessageBytes[true],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[tenantid],
> valueB=@MessageBytes[],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[accept],
> valueB=@MessageBytes[application/json, text/plain, */*],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[unity-access-token],
> valueB=@MessageBytes[f7e20127-1b55-4e1e-a0e2-d15b2caa112e],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[devicetype],
> valueB=@MessageBytes[],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[user-agent],
> valueB=@MessageBytes[HolliView],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[accept],
> valueB=@MessageBytes[application/json, text/plain, */*],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[content-type],
> valueB=@MessageBytes[application/json],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[token],
> valueB=@MessageBytes[f7e20127-1b55-4e1e-a0e2-d15b2caa112e],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[projectid],
> valueB=@MessageBytes[],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[referer],
> valueB=@MessageBytes[http://12.52.41.10:8080/hv-visio/display.html?tag=displays/%E7%AC%AC%E5%9B%9B%E5%87%80%E5%8C%96%E5%8E%82%E6%B5%81%E7%A8%8B%E5%9B%BE/%E8%84%B1%E7%A1%AB%E8%84%B1%E6%B0%B4%E8%A3%85%E7%BD%AE2/%E8%84%B1%E6%B0%B4%E8%A3%85%E7%BD%AE.json],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[accept-encoding],
> valueB=@MessageBytes[gzip, deflate],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[accept-language],
> valueB=@MessageBytes[zh-CN],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[forwarded],
> valueB=@MessageBytes[proto=http;host="12.52.41.10:8080";for="12.52.41.20:56611"],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[host],
> valueB=@MessageBytes[127.0.0.1:8001],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[x-forwarded-proto],
> valueB=@MessageBytes[http],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[x-forwarded-prefix],
> valueB=@MessageBytes[/hv-data],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[x-forwarded-port],
> valueB=@MessageBytes[8080],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[x-forwarded-host],
> valueB=@MessageBytes[12.52.41.10:8080],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[host],
> valueB=@MessageBytes[127.0.0.1:8001],
> ],
> @MimeHeaderField[
> nameB=@MessageBytes[content-length],
> valueB=@MessageBytes[0],
> ]
> ],
>
>
>
>
> Key observations:
> 1. The original request contains x-forwarded-for: 12.52.41.200.
> 2. The original request has host: 127.0.0.1:8001 only once.
> 3. However, after parsing, X-Forwarded-For is lost and Host becomes two.
> 4. It doesn't occur every time.
> 5.The Tomcat that is embedded within the Spring Boot
> 6. Trigger badRequest("http11processor.request.multipleHosts");
> 7.There are no other additional configurations.
>
>
> Investigations completed:
> - Confirmed that the header is included in the request flow before it reaches Tomcat.
> - Configured with RemoteIpValve, but the issue occurred during the parsing stage.
What components are in the mix before the request reaches Tomcat? The
formatting is a little odd in your original posting (e.g. the
X-Forwarded-For and X-Forwarded-Proto headers appear to be on the same
line).
> 1. Is this a known parsing issue?
> 2. Are there any configuration items that would affect the header parsing behavior?
> 3. How can we further determine whether it is a parsing logic issue or caused by pre-processing?
This is a really old version of Tomcat. Can you...
a. Upgrade to a later version of Spring Boot
b. Upgrade the Tomcat used by Spring Boot
?
-chris