We have a setup which looks like this:

AWS ELB --> Undertow Service A with ProxyHandler --> Undertow Service B

Traffic comes in to the ELB on HTTPS, the SSL gets terminated, and HTTP traffic flows through to service A with the X-Forwarded-Proto header set to https. We then proxy HTTP traffic through to service B, which is responsible for (amongst other things) generating links. In order to generate these links it needs to know the scheme/protocol on which to make requests from the internet to the ELB. 

Sadly, it looks like the Undertow proxy handler is clobbering this information by naively assuming that the request scheme used to hit this Undertow service should be the forwarded proto (ProxyHandler lines 434-436):

// Set the protocol header and attachment
final String proto = exchange.getRequestScheme().equals("https") ? "https" : "http";
request.getRequestHeaders().put(Headers.X_FORWARDED_PROTO, proto);

This does not check to see if there is a pre-existing proto header already set. In our case there is one, and it is set to https, but once it passes through this block, it has been rewritten to http, which is incorrect.

I noticed that higher up in the ProxyHandler logic where it handled X-Forwarded-For it actually employs some logic (configurable) to not clobber any existing value. I get why this is - because it can be a list of addresses - but is there some reason why we wouldn't want a similar check for the original protocol?

Our workaround for this is to copy the proto header into a custom header via a HttpHandler, ensuring that it gets copied from the incoming exchange to the outgoing exchange without being touched by Undertow, but this is a bit of a hack. Ideally we'd prefer any pre-existing proto header to be preserved, and to only use the request scheme as the fallback when no header exists.

Cheers,
Shannon