We have a Spring Boot project which serves up HTML pages using Thymeleaf. This is running behind a proxy. When we use RedirectView
from an HTTPS page, the redirect is returning the correct page, but on HTTP.
Our code is along the lines of:
@PostMapping(value = "/fillform")
public Object postForm(@Valid FormData formData)
{
return new RedirectView("formcompleted");
}
We do have the property set:
server.forward-headers-strategy=framework
Indeed that property does allow us to get the correct address (rather than the internal address the proxy uses). The issue is only with the schema. Also, the proxy we are using is Spring Cloud Gateway.
Comment From: philwebb
I'm not sure if this issue lies with Spring Framework or Spring Cloud Gateway. I'm afraid Spring Boot does little more than configure things, so I'm doubtful that we have an issue we can fix in Spring Boot itself.
@dlvenable Are you able to debug your application and look at what org.springframework.web.filter.ForwardedHeaderFilter
is doing? I'm specifically interested if ForwardedHeaderExtractingRequest.scheme
is being set correctly (UriComponentsBuilder.fromHttpRequest
should process the X-Forwarded-Proto
header.
@spencergibb is it possible that the X-Forwarded-Proto
isn't being included by Spring Cloud Gateway?
Comment From: spencergibb
It is included be default unless is has been disabled https://github.com/spring-cloud/spring-cloud-gateway/blob/master/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/filter/headers/XForwardedHeadersFilter.java#L220
Comment From: rstoyanchev
Can you provide more details including sample URL and "forwarded" type headers received by your app as well as the resulting redirect URL? Or provide a small sample (without a proxy) with sample input.
Comment From: dlvenable
Thank you for some of the suggestions.
I don't see any logging in org.springframework.web.filter.ForwardedHeaderFilter
that I can make use of.
However, I did add logging of the HttpServerRequest
in our actual controllers to work on tracking down the issue. I log the isSecure
flag and scheme
values.
For the GET request to load the page with the form, we get this logged (partial headers):
HTTP Servlet Request: isSecure=false, scheme=http
host: 172.xxx.xxx.xxx:8284
X-Forwarded-For: 136.xxx.xxx.xxx, 10.xxx.xxx.xxx,127.0.0.1
X-Real-IP: 10.xxx.xxx.xxx
For the POST for the form itself:
Simulation form received
HTTP Servlet Request: isSecure=false, scheme=http
referer: https://CORRECT_URL/development/testing/form
origin: https://CORRECT_URL
host: 172.xxx.xxx.xxx:8284
X-Forwarded-For: 136.xxx.xxx.xxx, 10.xxx.xxx.xxx,127.0.0.1
content-type: application/x-www-form-urlencoded
Content-Length: 80
X-Real-IP: 10.xxx.xxx.xxx
Redirecting: 302 Location: http://CORRECT_URL/development/testing/complete
That last line is our own filter which logs out any redirect responses along with the URL.
It appears we don't have any X-Forwarded
headers or a Forwarded
coming into the service from Spring Cloud Gateway. We are able to correctly redirect to URL - only the scheme is incorrect. So I'm not even sure how Spring knows this. Is it using the Origin
header or the Referer
?
Comment From: dlvenable
I took another look at org.springframework.web.filter.ForwardedHeaderFilter
and found that it removes headers from the HttpServletRequest
. Thus I started using Tomcat's RequestDumperFilter
to get all the headers rather than list them in my controller. Now I am seeing the Forwarded headers.
These are what I think are the relevant lines.
header=X-Forwarded-Host=CORRECT_URL
header=X-Forwarded-Proto=https,http
header=X-Forwarded-Port=443,80
header=Forwarded=proto=http;host=CORRECT_URL;for="127.0.0.1:40886"
header=X-Forwarded-For=136.xxx.xxx.xxx, 10.xxx.xxx.xxx,127.0.0.1
scheme=http
isSecure=false
Here is the full set of log lines.
START TIME =06-May-2020 16:31:02
requestURI=/development/testing/form
authType=null
characterEncoding=null
contentLength=80
contentType=application/x-www-form-urlencoded
contextPath=
header=sec-fetch-mode=navigate
header=referer=https://CORRECT_URL/development/testing/form
header=sec-fetch-site=same-origin
header=accept-language=en-US,en;q=0.9,es;q=0.8
header=origin=https://CORRECT_URL
header=X-Forwarded-Host=CORRECT_URL
header=X-Forwarded-Proto=https,http
header=sec-fetch-user=?1
header=X-Forwarded-Port=443,80
header=accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
header=host=172.xxx.xxx.xxx:8284
header=Forwarded=proto=http;host=CORRECT_URL;for="127.0.0.1:40886"
header=upgrade-insecure-requests=1
header=X-Forwarded-For=136.xxx.xxx.xxx, 10.xxx.xxx.xxx,127.0.0.1
header=content-type=application/x-www-form-urlencoded
header=cache-control=max-age=0
header=Content-Length=80
header=X-Real-IP=10.xxx.xxx.xxx
header=accept-encoding=gzip, deflate, br
header=sec-fetch-dest=document
locale=en_US
method=POST
parameter=value2=
parameter=_csrf=XXXX
parameter=redirectType=relativeRedirect
pathInfo=null
protocol=HTTP/1.1
queryString=null
remoteAddr=10.xxx.xxx.xxx
remoteHost=10.xxx.xxx.xxx
remoteUser=null
requestedSessionId=XXXX
scheme=http
serverName=172.xxx.xxx.xxx
serverPort=8284
servletPath=/development/testing/form
isSecure=false
------------------=--------------------------------------------
Simulation form received
HTTP Servlet Request: isSecure=false, scheme=http
Redirecting: 302 Location: http://CORRECT_URL/development/testing/complete
Comment From: rstoyanchev
It's been a while here, but the scheme is specified in two headers. Forwarded
with proto=http
and X-Forwarded-Proto=https,http
. Generally, we process Forwarded
first as the official standard, and only if we don't find it do we look at X-Forwarded-*
headers. In other words, from a Spring Framework perspective this is working as expected. ForwardedHeaderFilter
uses the scheme specified in the Forwarded
header.
I don't see anything further we can do in Spring Framework, so I'm closing the issue, but you can comment further or open an issue elsewhere as needed.