Spec compliance when converting HTTP/2 request to HTTP/1.1

In my case the user agent uses HTTP/2, my Node.js upstream uses HTTP/1.1.
The user agent sends only the :authority pseudo header, but not the Host header, which is the correct behaviour.
By not using either “header_upstream Host {host}” or the preset transparent, however, Caddy sets the Host header to the address of the upstream, when proxying the incoming requests.

Correct me if I am wrong, but I believe that this default behaviour is not compliant with the spec which states:

An intermediary that converts an HTTP/2 request to HTTP/1.1 MUST create a Host header field if one is not present in a request by copying the value of the “:authority” pseudo-header field.

Should not Caddy by default create the Host header by copying the value of :authority? What are your thoughts on this?
Thanks, Laszlo

The spec says, “if one is not present” – but Caddy does not make upstream requests without a Host header present (i.e. one is always set). So I believe we are still compliant…

Can you clarify, does it work properly if you set transparent (or that header_upstream line)?

Yes, it does work properly with both ‘transparent’ and header_upstream.

but Caddy does not make upstream requests without a Host header present

IMO, the spec here refers to the request between the browser and the intermediary. That is an HTTP/2 request, made by the browser, where the :authority pseudo header is present but the Host header is clearly not.
Caddy acts as an intermediary here, converting the HTTP/2 requests to HTTP/1.1. Therefore, according to the spec, it should copy the :authority header of the HTTP/2 request, when setting the Host header and making an HTTP/1.1 request against the Node.js upstream.

The spec is ambiguous, for sure.

Regardless of whether the Host is present in the downstream request, the upstream request never has an empty Host header (unless Caddy is configured explicitly to set it to an empty value) – if it is empty, then that’s probably a bug, and should be fixed.

To be honest, I’m a bit puzzled, because I just can’t see the ambiguity here. It might be because I’m not a native English speaker, and missed some nuances of the spec.

But let me put this thing differently:
The reason why Host header exists at all, according to the section 5.4 of HTTP/1.1 spec, is the following:

The “Host” header field in a request provides the host and port information from the target URI, enabling the origin server to distinguish among resources while servicing requests for multiple host names on a single IP address.

By default, Caddy creates a Host header, whose value is equal to the address of the upstream, and throwing away the :authority header, which contains the very information the Host header must contain in an HTTP/1.1 request. By doing so, it renders the Host header useless, which, in turn, prevents my Node.js origin server to use a single IP address for multiple host names. Therefore, defeating the very purpose of sending Host headers at the first place.

IMO, this it the reasoning why the HTTP/2 spec requires intermediaries to copy the value of the :authority pseudo header when creating the Host header for the HTTP/1.1 request.

If you need that information, you can set the transparent preset and the proxy will preserve this for you. So I’m not sure what the problem is?

Actually, I am using “header_upstream Host {host}” property. I might be wrong on this, but what I am trying to say here, is that both of those workarounds seem unnecessary configurations just to get the expected behaviour. They’re like the band-aids to the iPhone 4.

Oh, so you’re actually suggesting that the transparent preset should be the default setting?

I have not had the need yet to read about the other three headers in detail, but based on what I have found about the Host header in the spec, yes, I believe that “header_upstream Host {host}” setting should be the default.

Thanks for the discussion! Can you make an issue on https://github.com/mholt/caddy/issues so we don’t forget to consider this?

It’s done. Thank you for your help.
Regards, Laszlo

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.