Spring HATEOAS doesn't currently support the X-Forwarded-Prefix header.

NOTE: Examples are illustrative

We are using a reverse proxy / API gateway to serve a HATEOAS webflux project. Our API gateway accepts requests at:

https://api.example.com/context/resource

Our backend service address for this is:

https://backend.example.com/resource

It returns the following HATEOAS links

{
  "_links": {
    "self": {
      "href": "https://backend.example.com/resource"
    }
  }
}

By using the following headers:

X-Forwarded-Host: api.example.com
X-Forwarded-Prefix: /context

We would expect the result to be

{
  "_links": {
    "self": {
      "href": "https://api.example.com/context/resource"
    }
  }
}

Instead we are seeing that the X-Forwarded-Host header is being applied correctly, but the X-Forwarded-Prefix header is not, e.g.:

{
  "_links": {
    "self": {
      "href": "https://api.example.com/resource"
    }
  }
}

The WebFluxLinkBuilder used to create HATEOAS links leverages the UrlComponentsBuilder from spring-web, which is missing the handling logic for the X-Forwarded-Prefix header. The ForwardedHeaderTransformer (also in spring-web) does handle this header.

I'm proposing having this added to the UrlComponentsBuilder.

Comment From: rstoyanchev

ForwardedHeaderTransformer uses the forwarded prefix to set the contextPath and that's separate from the URI computed with UriComponentsBuilder so it's a bit of a challenge to change this without introducing an overloaded method on UriComponentsBuilder which I would rather avoid.

In general the recommended way of doing this is to have the ForwardedHeaderTransformer in place which will do it from on place and then remove the headers, to prevent further interpretations. This is more efficient, consistent, and safer.

Comment From: rstoyanchev

I have just noticed #24728. That's a breaking change for code that relies on request.getPath().contextPath().

Comment From: stuartwakefield

Good shout, yes I think ForwardedHeaderTransformer is the way to go, once I add this bean in, the output from UrlComponentsBuilder used in the WebFluxLinkBuilder is correct as it is handled a bit further upstream.

The issue is actually a bit deeper in the spring-hateoas project, even though the UrlComponentsBuilder returns the correct result, it is being overwritten when WebHandler#linkTo tries to map the method invocation to a URL. I'll close this PR and open a new set of changes in the spring-hateoas project.

Thanks 😄

Comment From: stuartwakefield

Yeah OK, seeing the problem now. We lose information with the ForwardedHeaderTransformer. By time we are constructing links the other end, the information that there was a prefix is lost as it just appears as part of the path. So when the WebHandler constructs links it is unaware that it needs to apply the X-Forwarded-Prefix, as the fact that this existed as part of the request is lost.

Most likely will need to retain the original request as a field on the transformed request so that this can be detected and used.

Comment From: stuartwakefield

May reopen with new changes.

Comment From: rstoyanchev

Can you elaborate? I don't quite follow what is lost.

It is true that ForwardedHeaderTransformer removes all forwarded headers it applies, but that's because they are now reflected in the contextPath and URI of the request, and their presence in the headers could lead to issues if any other code tries to process them again.

Comment From: hughwphamill

Was this ever addressed? @stuartwakefield ?