Affects: Spring Webflux 5.3.20
Running our app in our kubernetes cluster (or simulating it via spring.main.cloud-platform=kubernetes
) or using server.forward-headers-strategy=native
produces the wrong scheme when calling ServerWebExchange.getRequest().getURI()
when forward headers are present.
This seems to be because ReactorServerHttpRequest
uses request.scheme()
(which netty fills from X-Forwarded-Proto
if present), and combines it with the Host
header, where there's no clear correlation between those two headers.
Effectively, the following cURL request:
curl -H "X-Forwarded-Proto: https" -H "X-Forwarded-Host: my-gateway" -H "X-Forwarded-Port: 8181" http://localhost:8080/print-uri
... will return ServerWebExchange.getRequest().getURI() = https://localhost:8080/print-uri
, even though the scheme used for the request was HTTP.
Minimal Controller Example: https://github.com/mabako/spring-native-forwarding-headers-webflux/blob/master/src/main/java/com/example/demo/PrintUriController.java
Comment From: rstoyanchev
Thanks for the sample.
Indeed, we take the scheme from the Reactor Netty HttpServerRequest
, which reflects forwarded headers, but the "Host" header, which we use for the host and port, does not:
https://github.com/spring-projects/spring-framework/blob/d7824c7831eebbce69efeb6ad7331a7512b56070/spring-web/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java#L80-L114
It looks like up until 5cac619e237f8f71208142ad78c06a7d15b34c72 before we were using the remoteAddress
incorrectly (itt's the client address). At that point we switched to using to the "Host" header, falling back on the localAddress
which does reflect forwarded headers, but only if the "Host" header is not present.
@violetagg what is your recommendation for how we should be getting the host and address? Is there a reason to look at the "Host" header, or should we always use request.hostAddress()
?
/cc @bclozel
Comment From: violetagg
@rstoyanchev I think that X-Forwarded-Proto
is the scheme between the client and the proxy/load balancer, isn't it?
The X-Forwarded-Proto (XFP) header is a de-facto standard header for identifying the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer. Your server access logs contain the protocol used between the server and the load balancer, but not the protocol used between the client and the load balancer. To determine the protocol used between the client and the load balancer, the X-Forwarded-Proto request header can be used.
X-Forwarded-Host
should be the Host
header
The X-Forwarded-Host (XFH) header is a de-facto standard header for identifying the original host requested by the client in the [Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host) HTTP request header.
Recently we exposed all information:
See https://projectreactor.io/docs/netty/release/api/reactor/netty/http/server/ConnectionInformation.html
It is available from HttpServerRequest
and HttpServerResponse
Comment From: rstoyanchev
@violetagg, for the scheme, we use request#scheme()
and it reflects X-Forwarded-Proto. In the above example, it is "https", which is the original between client and proxy. This part works as expected, no problem that I see there.
The Host header is "localhost:8080", and not the "mygateway:8181" from the forwarded headers. Is this expected? If not you can run the sample and have a closer look. If it is, then I'm wondering if we should just stop parsing the "Host" header ourselves, and use the API you suggested, ConnectionInformation
instead.