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.